mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddac08bcbc | ||
|
|
517e285bb3 | ||
|
|
81a08a6b51 | ||
|
|
4dfef1d032 | ||
|
|
5d38e28cae | ||
|
|
66a12d10c4 | ||
|
|
1a4027c8d5 | ||
|
|
2a0243c51a | ||
|
|
4851d09e70 | ||
|
|
3cdf3accfd | ||
|
|
fd1f094216 | ||
|
|
25ece1ecb8 | ||
|
|
802e65c280 | ||
|
|
882688f056 | ||
|
|
97d60d0ab3 | ||
|
|
9ef99daed1 | ||
|
|
71c85d9d39 | ||
|
|
b981ab46be | ||
|
|
016633e494 | ||
|
|
1db862ab56 | ||
|
|
3563786310 | ||
|
|
ac8851dd6a | ||
|
|
53ab2d9793 | ||
|
|
2a58c86ffc | ||
|
|
fa46d527ae | ||
|
|
667bd38305 | ||
|
|
faba439466 | ||
|
|
c301157758 | ||
|
|
8d09d425a4 | ||
|
|
11f03d5a10 | ||
|
|
83625202a1 | ||
|
|
eb4b715a88 | ||
|
|
89dce941cf | ||
|
|
6371b67e9e | ||
|
|
966e27418a | ||
|
|
4adf7d1734 | ||
|
|
1a4c4a6fe8 | ||
|
|
7ff36592ae | ||
|
|
1b513412d2 | ||
|
|
f2bb43fbcd | ||
|
|
df5d07d885 | ||
|
|
0ad2f33c4c | ||
|
|
dee48fccb3 | ||
|
|
9ca372ab13 | ||
|
|
58a849a902 | ||
|
|
895072ec35 | ||
|
|
2b2c4d2c39 | ||
|
|
d534b6e2e8 | ||
|
|
783e9d9931 | ||
|
|
a109d9b55b | ||
|
|
5ce59ce44f | ||
|
|
1e9195a9bc | ||
|
|
3099d16468 | ||
|
|
c9c6af346c | ||
|
|
af80a43628 | ||
|
|
dc3c98ec91 | ||
|
|
dffa2c83f6 | ||
|
|
dc66f253a7 | ||
|
|
17498cbfc2 | ||
|
|
96e4d30168 | ||
|
|
c9d1934bb9 | ||
|
|
8bec083194 | ||
|
|
abf047be23 | ||
|
|
4a95399d56 | ||
|
|
885bf5154e | ||
|
|
3e146fc496 | ||
|
|
95710bcaf5 | ||
|
|
edd620f17d | ||
|
|
9c466d8b1e | ||
|
|
b385ab70fb | ||
|
|
140224840b | ||
|
|
2078cd5a5f |
No files matched your search
@@ -0,0 +1,56 @@
|
||||
name: Memory leak
|
||||
description: Memory leak caused by TAB.
|
||||
labels: ["Type: Memory leak"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server version
|
||||
description: Version and type of used server software where TAB is installed.
|
||||
placeholder: Spigot 1.18.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: TAB version
|
||||
description: Full version in numbers, "latest" is not a version.
|
||||
placeholder: 3.0.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Heap dump
|
||||
description: Link to uploaded memory dump which I can look into.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Uncleared object(s)
|
||||
description: Objects you believe should have been removed from the memory but are not
|
||||
placeholder: |
|
||||
There is only 1 player online, but heap dump has 100 instances of TabPlayer
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Let's make sure this report is valid
|
||||
options:
|
||||
- label: I am running latest version of the plugin
|
||||
required: true
|
||||
- label: I have included download link of my heap dump
|
||||
required: true
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
- label: I have explained what I believe is wrong with the heap dump
|
||||
required: true
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Performance issue
|
||||
description: Performance issue in TAB or in server caused by TAB
|
||||
labels: ["Type: Performance"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server version
|
||||
description: Version and type of used server software where TAB is installed.
|
||||
placeholder: Spigot 1.18.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: TAB version
|
||||
description: Full version in numbers, "latest" is not a version.
|
||||
placeholder: 3.0.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Evidence
|
||||
description: Any evidence proving the performance issue, such as output of /tab cpu or timings.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce (if known)
|
||||
description: What to do in order to reproduce this issue
|
||||
placeholder: |
|
||||
Step 1: ..
|
||||
Step 2: ..
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Let's make sure this report is valid
|
||||
options:
|
||||
- label: I am running latest version of the plugin
|
||||
required: true
|
||||
- label: I have included sufficient evidence of the issue
|
||||
required: true
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
@@ -7,18 +7,70 @@ More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/
|
||||
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
|
||||
version anymore. Only one version of the plugin exists now, which contains all implemented features
|
||||
and is available to everyone for free. However, customer support is only provided to
|
||||
customers who have purchased the plugin.
|
||||
customers who have purchased the plugin (see [Customer support](#customer-support)).
|
||||
|
||||
# Compiling
|
||||
Compilation requires JDK 8 and up.
|
||||
To compile the plugin, run `./gradlew build` from the terminal.
|
||||
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
|
||||
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
|
||||
The universal jar contains all modules for all supported platforms.
|
||||
|
||||
# Links
|
||||
Download the plugin for free: [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
|
||||
[BuiltByBit](https://builtbybit.com/resources/20631/), [Modrinth](https://modrinth.com/plugin/tab-was-taken)
|
||||
Support my work & receive customer support: [BuiltByBit](https://builtbybit.com/resources/14009/), [Polymart](https://polymart.org/resource/484)
|
||||
Other links: [Discord support for customers](https://discord.gg/EaSvdk6), [Wiki](https://github.com/NEZNAMY/TAB/wiki)
|
||||
# Customer support
|
||||
Unlike with other projects where requirements and level of support provided are unclear,
|
||||
we provide a clear answer to what kind of service you will or will not receive. Some don't
|
||||
even clearly state if they are free or paid. While they are listed as paid on spigot, they have
|
||||
a free download link in description, which can be confusing.
|
||||
|
||||
Free plugins typically do have a support discord, however you will rarely receive
|
||||
official support from the developers, especially for large projects with tons of users, where
|
||||
helping everyone in the world (theoretically) would consume too much time, leaving authors
|
||||
no time left to actually work on the project.
|
||||
In the end, they are free, made by people in their free time, and they are allowed to choose
|
||||
what they do or don't want to do in their free time. They often leave support to other people,
|
||||
possibly staff members, who are, however, in the end just more volunteers who may not even know
|
||||
the answer.
|
||||
|
||||
With paid plugins you are more likely to receive support, but unless explicitly said, nothing is
|
||||
guaranteed. There are lots of paid plugins where authors don't provide any support or redirect
|
||||
it to helpers, where again they are unpaid volunteers who might not even know the answer.
|
||||
|
||||
In TAB, everything is clearly defined. The plugin is free to download for everyone.
|
||||
Customer support, however, is only provided to customers who have formally purchased a copy
|
||||
of the plugin on any of the websites the plugin is being sold on:
|
||||
* [BuiltByBit](https://builtbybit.com/resources/14009/) (accepts any payment method)
|
||||
* [Polymart](https://polymart.org/resource/484) (only PayPal)
|
||||
|
||||
After purchasing, you can request assistance via either private messages on the website where you
|
||||
have purchased it, or using [TAB's Discord server](https://discord.gg/EaSvdk6) (which is highly
|
||||
preferred, since the communication will be much faster thanks to notifications and more).
|
||||
* We use a ticket system, which allows us to effectively track any amount of unresolved problems.
|
||||
No more questions getting lost in the sea of messages from other people asking. Tickets are only closed
|
||||
after user confirmed all of their questions were answered and then receive a transcript of the ticket
|
||||
from the bot after ticket is closed into private messages (unless user is blocking them).
|
||||
* We do not rely on the community helping, which often makes things even worse. We know people have
|
||||
better things to do than helping random people with a random plugin, especially when most people only
|
||||
have a very basic knowledge of the plugin, not being able to answer more complicated problems.
|
||||
* We have notifications enabled and reply as soon as possible. We do not have the server muted unlike
|
||||
most mods on most servers who only check it when they are really bored, give a wiki link and
|
||||
disappear again.
|
||||
* As the developer, I do not rely on helpers providing support instead of me. They are handy as
|
||||
I am obviously not available 24/7, but I do not intentionally leave anything to them. I reply
|
||||
whenever I am available without any intentional delays.
|
||||
|
||||
The wiki contains everything about the plugin in one way or another, so support should not
|
||||
even be needed. It also contains a few examples, but obviously cannot contain a pre-made config
|
||||
for every possible idea anyone can come up with.
|
||||
Support is provided to people who decided paying for support is more time efficient than reading the wiki,
|
||||
especially for serious servers, where price of the plugin is nothing compared to other server costs.
|
||||
|
||||
# Other links
|
||||
Download the plugin for free:
|
||||
* [GitHub releases](https://github.com/NEZNAMY/TAB/releases)
|
||||
* [BuiltByBit](https://builtbybit.com/resources/20631/)
|
||||
* [Modrinth](https://modrinth.com/plugin/tab-was-taken)
|
||||
|
||||
[Wiki](https://github.com/NEZNAMY/TAB/wiki)
|
||||
[Donate](https://paypal.me/neznamy1) if you are feeling extra generous
|
||||
|
||||
# Issues
|
||||
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Security Policy
|
||||
If you found a security issue, send me a private message anywhere.
|
||||
@@ -12,6 +12,7 @@ dependencies {
|
||||
compileOnlyApi(libs.adventureMiniMessage)
|
||||
compileOnlyApi(libs.adventureApi)
|
||||
compileOnlyApi(libs.adventureLegacy)
|
||||
compileOnlyApi(libs.via)
|
||||
}
|
||||
|
||||
blossom {
|
||||
@@ -20,6 +21,7 @@ blossom {
|
||||
replaceToken("@version@", project.version)
|
||||
replaceToken("@description@", project.description)
|
||||
replaceToken("@website@", rootProject.ext.get("website")!!.toString())
|
||||
replaceToken("@author@", rootProject.ext.get("author")!!.toString())
|
||||
replaceTokenIn("src/main/java/me/neznamy/tab/api/TabConstants.java")
|
||||
}
|
||||
|
||||
|
||||
@@ -47,28 +47,7 @@ public interface ArmorStand {
|
||||
* player to send packet to
|
||||
*/
|
||||
void teleport(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Changes sneaking flag and sends packets to everyone in range
|
||||
*
|
||||
* @param sneaking
|
||||
* new sneaking status
|
||||
*/
|
||||
void sneak(boolean sneaking);
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for everyone
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for specified player
|
||||
*
|
||||
* @param viewer
|
||||
* player to send packet to
|
||||
*/
|
||||
void destroy(TabPlayer viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Updates armor stand's name if needed
|
||||
*/
|
||||
@@ -96,13 +75,4 @@ public interface ArmorStand {
|
||||
* player to spawn for
|
||||
*/
|
||||
void spawn(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Sends respawn packets to specified player to make position
|
||||
* change instant instead of teleport animation
|
||||
*
|
||||
* @param viewer
|
||||
* Player to send armor stand respawn packets to
|
||||
*/
|
||||
void respawn(TabPlayer viewer);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Enum class representing all possibly used protocol versions
|
||||
*/
|
||||
@@ -11,6 +14,7 @@ public enum ProtocolVersion {
|
||||
UNKNOWN_SERVER_VERSION ("Unknown"),
|
||||
UNKNOWN_CLIENT_VERSION ("Unknown"),
|
||||
PROXY ("Proxy"),
|
||||
V1_19_4 (762),
|
||||
V1_19_3 (761),
|
||||
V1_19_2 (760),
|
||||
V1_19_1 (760),
|
||||
@@ -137,4 +141,19 @@ public enum ProtocolVersion {
|
||||
}
|
||||
return UNKNOWN_CLIENT_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets player's network version using ViaVersion API
|
||||
*
|
||||
* @param player
|
||||
* Player's UUID
|
||||
* @param playerName
|
||||
* Player's name for debug messages
|
||||
* @return Player's network version
|
||||
*/
|
||||
public static int getPlayerVersionVia(UUID player, String playerName) {
|
||||
int version = Via.getAPI().getPlayerVersion(player);
|
||||
TabAPI.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public class TabConstants {
|
||||
public static final String PLUGIN_VERSION = "@version@";
|
||||
public static final String PLUGIN_DESCRIPTION = "@description@";
|
||||
public static final String PLUGIN_WEBSITE = "@website@";
|
||||
public static final String PLUGIN_AUTHOR = "@author@";
|
||||
|
||||
public static final String NO_GROUP = "NONE";
|
||||
public static final String DEFAULT_GROUP = "_DEFAULT_";
|
||||
@@ -347,6 +348,7 @@ public class TabConstants {
|
||||
public static final String VAULT = "Vault";
|
||||
public static final String PLACEHOLDER_API = "PlaceholderAPI";
|
||||
public static final String LIBS_DISGUISES = "LibsDisguises";
|
||||
public static final String VIAREWIND = "ViaRewind";
|
||||
public static final String VIAVERSION = "ViaVersion";
|
||||
public static final String PROTOCOL_SUPPORT = "ProtocolSupport";
|
||||
public static final String ESSENTIALS = "Essentials";
|
||||
|
||||
@@ -2,7 +2,7 @@ package me.neznamy.tab.api;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
@@ -14,12 +14,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
*/
|
||||
public abstract class TabFeature {
|
||||
|
||||
/** Feature's name displayed in /tab cpu */
|
||||
@Getter private final String featureName;
|
||||
|
||||
/** Feature's function name displayed in place of refreshing in /tab cpu */
|
||||
@Getter private final String refreshDisplayName;
|
||||
|
||||
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
|
||||
protected String[] disabledServers = new String[0];
|
||||
|
||||
@@ -44,19 +38,16 @@ public abstract class TabFeature {
|
||||
private final List<String> methodOverrides = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads method overrides
|
||||
*
|
||||
* @param featureName
|
||||
* Feature's name in /tab cpu
|
||||
* @param refreshDisplayName
|
||||
* "refreshing" cpu display type name of the feature
|
||||
* Constructs new instance and loads method overrides
|
||||
*/
|
||||
protected TabFeature(String featureName, String refreshDisplayName) {
|
||||
this.featureName = featureName;
|
||||
this.refreshDisplayName = refreshDisplayName;
|
||||
protected TabFeature() {
|
||||
try {
|
||||
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onCommand");
|
||||
if (getClass().getMethod("load").getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("load");
|
||||
if (getClass().getMethod("unload").getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("unload");
|
||||
if (getClass().getMethod("onJoin", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onJoin");
|
||||
if (getClass().getMethod("onQuit", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
@@ -95,16 +86,12 @@ public abstract class TabFeature {
|
||||
* Also loads lists of disabled worlds and servers of this feature with config
|
||||
* section path specified with {@code configSection} parameter.
|
||||
*
|
||||
* @param featureName
|
||||
* Feature's name in /tab cpu
|
||||
* @param refreshDisplayName
|
||||
* "refreshing" cpu display type name of the feature
|
||||
* @param configSection
|
||||
* Configuration section of the feature to load disabled
|
||||
* servers / worlds from
|
||||
*/
|
||||
protected TabFeature(String featureName, String refreshDisplayName, String configSection) {
|
||||
this(featureName, refreshDisplayName);
|
||||
protected TabFeature(@NonNull String configSection) {
|
||||
this();
|
||||
List<String> disabledServers = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-servers");
|
||||
List<String> disabledWorlds = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-worlds");
|
||||
if (disabledServers != null) {
|
||||
@@ -388,4 +375,19 @@ public abstract class TabFeature {
|
||||
public void removeDisabledPlayer(TabPlayer p) {
|
||||
disabledPlayers.remove(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns display name of {@link #refresh(TabPlayer, boolean)}
|
||||
* called for this feature in /tab cpu
|
||||
*
|
||||
* @return Display name of refresh function in this feature
|
||||
*/
|
||||
public String getRefreshDisplayName() { return null; }
|
||||
|
||||
/**
|
||||
* Returns name of this feature displayed in /tab cpu
|
||||
*
|
||||
* @return name of this feature display in /tab cpu
|
||||
*/
|
||||
public abstract String getFeatureName();
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package me.neznamy.tab.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
@@ -141,13 +140,6 @@ public interface TabPlayer {
|
||||
*/
|
||||
void sendMessage(IChatBaseComponent message);
|
||||
|
||||
/**
|
||||
* Returns player's netty channel. On servers running version 1.7 or older returns {@code null}.
|
||||
*
|
||||
* @return player's channel
|
||||
*/
|
||||
Channel getChannel();
|
||||
|
||||
/**
|
||||
* Returns player's ping calculated by server
|
||||
*
|
||||
|
||||
@@ -161,18 +161,4 @@ public class RGBUtils {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if text contains legacy color request at defined RGB index start
|
||||
*
|
||||
* @param text
|
||||
* text to check
|
||||
* @param i
|
||||
* current index start
|
||||
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
|
||||
*/
|
||||
private static boolean containsLegacyCode(String text, int i) {
|
||||
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
|
||||
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,7 @@ import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
@@ -16,6 +13,20 @@ import java.util.UUID;
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/** Map for faster EnumSet creation */
|
||||
private static final EnumMap<EnumPlayerInfoAction, EnumSet<EnumPlayerInfoAction>> actionToEnumSetMap =
|
||||
new EnumMap<EnumPlayerInfoAction, EnumSet<EnumPlayerInfoAction>>(EnumPlayerInfoAction.class) {{
|
||||
for (EnumPlayerInfoAction action : EnumPlayerInfoAction.values()) {
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
EnumSet<EnumPlayerInfoAction> set = EnumSet.allOf(EnumPlayerInfoAction.class);
|
||||
set.remove(EnumPlayerInfoAction.REMOVE_PLAYER);
|
||||
put(action, set);
|
||||
} else {
|
||||
put(action, EnumSet.of(action));
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
/** Packet actions */
|
||||
private final EnumSet<EnumPlayerInfoAction> actions;
|
||||
|
||||
@@ -43,16 +54,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(@NonNull EnumPlayerInfoAction action, @NonNull List<PlayerInfoData> entries) {
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
|
||||
EnumPlayerInfoAction.INITIALIZE_CHAT,
|
||||
EnumPlayerInfoAction.UPDATE_GAME_MODE,
|
||||
EnumPlayerInfoAction.UPDATE_LISTED,
|
||||
EnumPlayerInfoAction.UPDATE_LATENCY,
|
||||
EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
|
||||
} else {
|
||||
this.actions = EnumSet.of(action);
|
||||
}
|
||||
this.actions = actionToEnumSetMap.get(action);
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
* Class with utility methods used in the plugin
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Preconditions {
|
||||
|
||||
/**
|
||||
* Private constructor preventing the class from being instantiated
|
||||
*/
|
||||
private Preconditions() {}
|
||||
|
||||
/**
|
||||
* Checks length of String and throws {@code IllegalArgumentException} if it is
|
||||
* longer than limit.
|
||||
|
||||
+2
-1
@@ -4,11 +4,12 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "3.3.0"
|
||||
version = "3.3.1"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
ext.set("website", "https://github.com/NEZNAMY/TAB")
|
||||
ext.set("author", "NEZNAMY")
|
||||
}
|
||||
|
||||
val platforms = setOf(
|
||||
|
||||
@@ -3,14 +3,14 @@ package me.neznamy.tab.platforms.bukkit;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.protocol.*;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.*;
|
||||
import org.bukkit.Location;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutSpawnEntityLiving;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -18,10 +18,10 @@ import java.util.UUID;
|
||||
public class BukkitPacketBuilder extends PacketBuilder {
|
||||
|
||||
{
|
||||
buildMap.put(PacketPlayOutEntityMetadata.class, (packet, version) -> ((PacketPlayOutEntityMetadata)packet).build());
|
||||
buildMap.put(PacketPlayOutEntityTeleport.class, (packet, version) -> ((PacketPlayOutEntityTeleport)packet).build());
|
||||
buildMap.put(PacketPlayOutEntityDestroy.class, (packet, version) -> ((PacketPlayOutEntityDestroy)packet).build());
|
||||
buildMap.put(PacketPlayOutSpawnEntityLiving.class, (packet, version) -> ((PacketPlayOutSpawnEntityLiving)packet).build());
|
||||
buildMap.put(PacketPlayOutEntityMetadata.class, (packet, version) -> PacketPlayOutEntityMetadataStorage.build((PacketPlayOutEntityMetadata) packet));
|
||||
buildMap.put(PacketPlayOutEntityTeleport.class, (packet, version) -> PacketPlayOutEntityTeleportStorage.build((PacketPlayOutEntityTeleport) packet));
|
||||
buildMap.put(PacketPlayOutEntityDestroy.class, (packet, version) -> PacketPlayOutEntityDestroyStorage.build((PacketPlayOutEntityDestroy) packet));
|
||||
buildMap.put(PacketPlayOutSpawnEntityLiving.class, (packet, version) -> PacketPlayOutSpawnEntityLivingStorage.build((PacketPlayOutSpawnEntityLiving) packet));
|
||||
buildMap.put(PacketPlayOutPlayerListHeaderFooter.class, (packet, version) -> PacketPlayOutPlayerListHeaderFooterStorage.build((PacketPlayOutPlayerListHeaderFooter) packet, version));
|
||||
buildMap.put(PacketPlayOutChat.class, (packet, version) -> PacketPlayOutChatStorage.build((PacketPlayOutChat) packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardObjective.class, (packet, version) -> PacketPlayOutScoreboardObjectiveStorage.build((PacketPlayOutScoreboardObjective) packet, version));
|
||||
@@ -76,7 +76,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
|
||||
int entityId = packet.getId().hashCode();
|
||||
if (packet.getAction() == Action.REMOVE) {
|
||||
return new PacketPlayOutEntityDestroy(entityId).build();
|
||||
return build(new PacketPlayOutEntityDestroy(entityId), clientVersion);
|
||||
}
|
||||
DataWatcher w = new DataWatcher();
|
||||
if (packet.getAction() == Action.UPDATE_PCT || packet.getAction() == Action.ADD) {
|
||||
@@ -90,9 +90,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
if (packet.getAction() == Action.ADD) {
|
||||
w.getHelper().setEntityFlags((byte) 32);
|
||||
w.getHelper().setWitherInvulnerableTime(880); // Magic number
|
||||
return new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, new Location(null, 0,0,0), w).build();
|
||||
return build(new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, 0, 0, 0, 0, 0, w), clientVersion);
|
||||
} else {
|
||||
return new PacketPlayOutEntityMetadata(entityId, w).build();
|
||||
return build(new PacketPlayOutEntityMetadata(entityId, w), clientVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
@@ -18,14 +19,14 @@ import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutScoreboar
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutScoreboardObjectiveStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutScoreboardTeamStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Pipeline injection for bukkit
|
||||
*/
|
||||
public class BukkitPipelineInjector extends PipelineInjector {
|
||||
public class BukkitPipelineInjector extends NettyPipelineInjector {
|
||||
|
||||
/** NMS data storage */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
@@ -39,6 +40,17 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
super("packet_handler");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Channel getChannel(TabPlayer player) {
|
||||
final BukkitTabPlayer bukkit = (BukkitTabPlayer) player;
|
||||
try {
|
||||
if (nms.CHANNEL != null) return (Channel) nms.CHANNEL.get(nms.NETWORK_MANAGER.get(bukkit.getPlayerConnection()));
|
||||
} catch (final IllegalAccessException exception) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + bukkit.getName(), exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
@@ -53,7 +65,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
try {
|
||||
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
|
||||
super.channelRead(context, packet);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
}
|
||||
@@ -81,12 +93,12 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player, packet);
|
||||
}
|
||||
TAB.getInstance().getFeatureManager().onPacketSend(player, packet);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
try {
|
||||
super.write(context, packet, channelPromise);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -19,7 +21,8 @@ import me.neznamy.tab.platforms.bukkit.permission.Vault;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
@@ -32,6 +35,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -39,6 +43,7 @@ import java.util.Collection;
|
||||
/**
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlatform extends BackendPlatform {
|
||||
|
||||
@Getter private final BukkitPacketBuilder packetBuilder = new BukkitPacketBuilder();
|
||||
@@ -50,28 +55,9 @@ public class BukkitPlatform extends BackendPlatform {
|
||||
/** Variables checking presence of other plugins to hook into */
|
||||
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
|
||||
@Getter @Setter private boolean libsDisguisesEnabled = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
private Plugin viaVersion;
|
||||
private final boolean viaVersion = ReflectionUtils.classExists("com.viaversion.viaversion.api.Via");
|
||||
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given plugin parameter
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public BukkitPlatform(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
|
||||
if (ReflectionUtils.classExists("com.viaversion.viaversion.api.Via")) {
|
||||
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
|
||||
} else {
|
||||
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionPlugin detectPermissionPlugin() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LUCKPERMS)) {
|
||||
@@ -113,13 +99,13 @@ public class BukkitPlatform extends BackendPlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabExpansion getTabExpansion() {
|
||||
public @NotNull TabExpansion getTabExpansion() {
|
||||
if (placeholderAPI) {
|
||||
BukkitTabExpansion expansion = new BukkitTabExpansion();
|
||||
expansion.register();
|
||||
return expansion;
|
||||
}
|
||||
return null;
|
||||
return new EmptyTabExpansion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -205,8 +191,8 @@ public class BukkitPlatform extends BackendPlatform {
|
||||
//some PS versions return -1 on unsupported server versions instead of throwing exception
|
||||
if (version != -1 && version < TAB.getInstance().getServerVersion().getNetworkId()) return version;
|
||||
}
|
||||
if (viaVersion != null) {
|
||||
return getProtocolVersionVia(player.getUniqueId(), player.getName(), 0);
|
||||
if (viaVersion) {
|
||||
return ProtocolVersion.getPlayerVersionVia(player.getUniqueId(), player.getName());
|
||||
}
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ public class BukkitTAB extends JavaPlugin {
|
||||
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
|
||||
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
|
||||
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1",
|
||||
"v1_18_R2", "v1_19_R1", "v1_19_R2");
|
||||
"v1_18_R2", "v1_19_R1", "v1_19_R2", "v1_19_R3");
|
||||
String supportedVersionRange = "1.5 - 1.19.4";
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
@@ -81,13 +82,13 @@ public class BukkitTAB extends JavaPlugin {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
|
||||
return true;
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.2 - 1.19.3. Disabling just to stay safe."));
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on " + supportedVersionRange + ". Disabling just to stay safe."));
|
||||
}
|
||||
} catch (IllegalStateException ex) {
|
||||
if (supportedVersions.contains(serverPackage)) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report this issue (include your server version & fork too)"));
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.2 - 1.19.3. Disabling."));
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports " + supportedVersionRange + ". Disabling."));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
@@ -39,7 +39,7 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
private Object handle;
|
||||
|
||||
/** Player's connection for sending packets, preloading for speed */
|
||||
private Object playerConnection;
|
||||
@Getter private Object playerConnection;
|
||||
|
||||
/** Bukkit BossBars the player can currently see */
|
||||
private final Map<UUID, BossBar> bossBars = new HashMap<>();
|
||||
@@ -284,15 +284,4 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
public int getGamemode() {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
try {
|
||||
if (NMSStorage.getInstance().CHANNEL != null)
|
||||
return (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
|
||||
} catch (IllegalAccessException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + getName(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+5
-8
@@ -8,7 +8,7 @@ import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -22,9 +22,9 @@ import java.util.WeakHashMap;
|
||||
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
|
||||
|
||||
/** Map holding all values for all players for easy and high-performance access */
|
||||
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
|
||||
private final WeakHashMap<Player, Map<String, String>> values = new WeakHashMap<>();
|
||||
|
||||
@Getter private final String author = "NEZNAMY";
|
||||
@Getter private final String author = TabConstants.PLUGIN_AUTHOR;
|
||||
@Getter private final String identifier = TabConstants.PLUGIN_ID;
|
||||
@Getter private final String version = TabConstants.PLUGIN_VERSION;
|
||||
|
||||
@@ -50,14 +50,11 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
|
||||
if (identifier.startsWith("placeholder_")) {
|
||||
TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", (TabFeature) TabAPI.getInstance().getPlaceholderManager());
|
||||
}
|
||||
if (player == null) return "<Player cannot be null>";
|
||||
TabPlayer p = TabAPI.getInstance().getPlayer(player.getUniqueId());
|
||||
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
|
||||
return values.get(p).get(identifier);
|
||||
return values.get(player).get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(TabPlayer player, String key, String value) {
|
||||
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
|
||||
values.computeIfAbsent((Player) player.getPlayer(), p -> new HashMap<>()).put(key, value);
|
||||
}
|
||||
}
|
||||
+8
-11
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -23,6 +25,7 @@ import me.neznamy.tab.shared.TAB;
|
||||
* Per-world-PlayerList feature handler
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@RequiredArgsConstructor
|
||||
public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
|
||||
/** Config options */
|
||||
@@ -30,21 +33,15 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
private final List<String> ignoredWorlds = TabAPI.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
private final Map<String, List<String>> sharedWorlds = TabAPI.getInstance().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public PerWorldPlayerList(JavaPlugin plugin) {
|
||||
super("Per world PlayerList", null);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
|
||||
}
|
||||
/** Plugin reference*/
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
@Getter private final String featureName = "Per world PlayerList";
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Bukkit.getOnlinePlayers().forEach(this::checkPlayer);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityMetadataStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
@@ -46,13 +46,7 @@ public class PetFix extends TabFeature {
|
||||
*/
|
||||
private static final int INTERACT_COOLDOWN = 160;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*/
|
||||
public PetFix() {
|
||||
super("Pet name fix", null);
|
||||
TabAPI.getInstance().debug("Loaded PetFix feature");
|
||||
}
|
||||
@Getter private final String featureName = "Pet name fix";
|
||||
|
||||
/**
|
||||
* Returns position of pet owner field based on server version
|
||||
@@ -121,9 +115,9 @@ public class PetFix extends TabFeature {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (PacketPlayOutEntityMetadata.CLASS.isInstance(packet)) {
|
||||
if (PacketPlayOutEntityMetadataStorage.CLASS.isInstance(packet)) {
|
||||
Object removedEntry = null;
|
||||
List<Object> items = (List<Object>) PacketPlayOutEntityMetadata.LIST.get(packet);
|
||||
List<Object> items = (List<Object>) PacketPlayOutEntityMetadataStorage.LIST.get(packet);
|
||||
if (items == null) return;
|
||||
try {
|
||||
for (Object item : items) {
|
||||
@@ -143,18 +137,18 @@ public class PetFix extends TabFeature {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removedEntry != null) items.remove(removedEntry);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
//no idea how can this list change in another thread since it's created for the packet but whatever, try again
|
||||
onPacketSend(receiver, packet);
|
||||
}
|
||||
if (removedEntry != null) items.remove(removedEntry);
|
||||
} else if (PacketPlayOutSpawnEntityLiving.CLASS.isInstance(packet) && PacketPlayOutSpawnEntityLiving.DATA_WATCHER != null) {
|
||||
} else if (PacketPlayOutSpawnEntityLivingStorage.CLASS.isInstance(packet) && PacketPlayOutSpawnEntityLivingStorage.DATA_WATCHER != null) {
|
||||
//<1.15
|
||||
DataWatcher watcher = DataWatcher.fromNMS(PacketPlayOutSpawnEntityLiving.DATA_WATCHER.get(packet));
|
||||
DataWatcher watcher = DataWatcher.fromNMS(PacketPlayOutSpawnEntityLivingStorage.DATA_WATCHER.get(packet));
|
||||
DataWatcherItem petOwner = watcher.getItem(petOwnerPosition);
|
||||
if (petOwner != null && (petOwner.getValue() instanceof Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
|
||||
watcher.removeValue(petOwnerPosition);
|
||||
PacketPlayOutSpawnEntityLiving.DATA_WATCHER.set(packet, watcher.build());
|
||||
PacketPlayOutSpawnEntityLivingStorage.DATA_WATCHER.set(packet, watcher.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -9,37 +16,25 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
|
||||
/**
|
||||
* An additional class with additional code for <1.9 servers due to an entity being required
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class WitherBossBar extends BossBarManagerImpl implements Listener {
|
||||
|
||||
/** Distance of the Wither in blocks */
|
||||
private static final int WITHER_DISTANCE = 60;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class and registers events and task
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public WitherBossBar(JavaPlugin plugin) {
|
||||
|
||||
/** Reference to plugin for registering listener */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
//when MC is on fullscreen, BossBar disappears after 1 second of not being seen
|
||||
//when in a small window, it's about 100ms
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(100,
|
||||
this, TabConstants.CpuUsageCategory.TELEPORTING_WITHER, this::teleport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
teleport();
|
||||
}
|
||||
@@ -54,7 +49,7 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
|
||||
if (!line.containsPlayer(p)) continue;
|
||||
Location loc = ((Player) p.getPlayer()).getEyeLocation().add(((Player) p.getPlayer()).getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
|
||||
if (loc.getY() < 1) loc.setY(1);
|
||||
p.sendCustomPacket(new PacketPlayOutEntityTeleport(line.getUniqueId().hashCode(), loc), TabConstants.PacketCategory.BOSSBAR_WITHER_TELEPORT);
|
||||
p.sendCustomPacket(new PacketPlayOutEntityTeleport(line.getUniqueId().hashCode(), loc.getX(), loc.getY(), loc.getZ(), 0, 0), TabConstants.PacketCategory.BOSSBAR_WITHER_TELEPORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
|
||||
/**
|
||||
* A class representing an armor stand attached to a player
|
||||
*/
|
||||
public class BukkitArmorStand extends BackendArmorStand {
|
||||
|
||||
/** Owner as a Bukkit player */
|
||||
private final Player player;
|
||||
|
||||
/** Entity destroy packet */
|
||||
protected final TabPacket destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param asm
|
||||
* Armor stand manager which this armor stand belongs to
|
||||
* @param owner
|
||||
* Owner of the armor stand
|
||||
* @param propertyName
|
||||
* Name of refresh property to use
|
||||
* @param yOffset
|
||||
* Offset in blocks
|
||||
* @param staticOffset
|
||||
* {@code true} if offset is static, {@code false} if not
|
||||
*/
|
||||
public BukkitArmorStand(BackendNameTagX feature, BackendArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
super(feature, asm, owner, propertyName, yOffset, staticOffset);
|
||||
player = (Player) owner.getPlayer();
|
||||
sneaking = player.isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(TabPlayer viewer) {
|
||||
for (TabPacket packet : getSpawnPackets(viewer)) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns location where armor stand should be at time of calling.
|
||||
* This takes into account everything that affects height, including
|
||||
* viewer's game version.
|
||||
*
|
||||
* @param viewer
|
||||
* Player looking at the armor stand
|
||||
* @return Location where armor stand should be for specified viewer
|
||||
*/
|
||||
public Location getLocation(TabPlayer viewer) {
|
||||
Location loc = player.getLocation();
|
||||
double y = loc.getY();
|
||||
|
||||
//1.14+ server sided bug
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
if (vehicle.getType().toString().contains("horse")) { //covering all 3 horse types
|
||||
y = vehicle.getLocation().getY() + 0.85;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("donkey")) { //1.11+
|
||||
y = vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
y = vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("strider")) { //1.16+
|
||||
y = vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
} else {
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
y = loc.getY()-1.22;
|
||||
}
|
||||
}
|
||||
y += getYAdd(player.isSleeping(), sneaking, viewer);
|
||||
return new Location(null, loc.getX(), y, loc.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetadata(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTeleportPacket(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityTeleport(entityId, getLocation(viewer)),
|
||||
TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if owner is swimming, {@code false} if not
|
||||
* @return {@code true} if owner is swimming, {@code false} if not
|
||||
*/
|
||||
private boolean isSwimming() {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
|
||||
private TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = calculateVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
*
|
||||
* @param displayName
|
||||
* armor stand name
|
||||
* @param viewer
|
||||
* player to apply checks against
|
||||
* @return DataWatcher for viewer
|
||||
*/
|
||||
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
|
||||
DataWatcher datawatcher = new DataWatcher();
|
||||
datawatcher.getHelper().setEntityFlags((byte) (sneaking ? 34 : 32));
|
||||
datawatcher.getHelper().setCustomName(displayName, viewer.getVersion());
|
||||
datawatcher.getHelper().setCustomNameVisible(!shouldBeInvisibleFor(viewer, displayName) && visible);
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x())
|
||||
datawatcher.getHelper().setArmorStandFlags((byte)16);
|
||||
return datawatcher;
|
||||
}
|
||||
}
|
||||
+67
-19
@@ -1,18 +1,20 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityDestroyStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityTeleportStorage;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@@ -23,6 +25,7 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* The core class for unlimited NameTag mode on Bukkit
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitNameTagX extends BackendNameTagX {
|
||||
|
||||
/** Reference to NMS storage for quick access */
|
||||
@@ -31,15 +34,13 @@ public class BukkitNameTagX extends BackendNameTagX {
|
||||
/** Bukkit event listener */
|
||||
private final EventListener eventListener = new EventListener(this);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter, loads config options, registers events
|
||||
* and registers sub-features.
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public BukkitNameTagX(JavaPlugin plugin) {
|
||||
/** Plugin reference */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
super.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,13 +68,13 @@ public class BukkitNameTagX extends BackendNameTagX {
|
||||
if (!receiver.isLoaded() || isDisabledPlayer(receiver) || getDisabledUnlimitedPlayers().contains(receiver)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
packetListener.onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
} else if (PacketPlayOutEntityTeleport.CLASS.isInstance(packet)) {
|
||||
packetListener.onEntityMove(receiver, PacketPlayOutEntityTeleport.ENTITY_ID.getInt(packet));
|
||||
} else if (PacketPlayOutEntityTeleportStorage.CLASS.isInstance(packet)) {
|
||||
packetListener.onEntityMove(receiver, PacketPlayOutEntityTeleportStorage.ENTITY_ID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
packetListener.onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
} else if (PacketPlayOutEntityDestroy.CLASS.isInstance(packet)) {
|
||||
} else if (PacketPlayOutEntityDestroyStorage.CLASS.isInstance(packet)) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
Object entities = PacketPlayOutEntityDestroy.ENTITIES.get(packet);
|
||||
Object entities = PacketPlayOutEntityDestroyStorage.ENTITIES.get(packet);
|
||||
if (entities instanceof List) {
|
||||
packetListener.onEntityDestroy(receiver, (List<Integer>) entities);
|
||||
} else {
|
||||
@@ -81,7 +82,7 @@ public class BukkitNameTagX extends BackendNameTagX {
|
||||
packetListener.onEntityDestroy(receiver, (int) entities);
|
||||
}
|
||||
} else {
|
||||
packetListener.onEntityDestroy(receiver, (int[]) PacketPlayOutEntityDestroy.ENTITIES.get(packet));
|
||||
packetListener.onEntityDestroy(receiver, (int[]) PacketPlayOutEntityDestroyStorage.ENTITIES.get(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +149,54 @@ public class BukkitNameTagX extends BackendNameTagX {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendArmorStand createArmorStand(BackendArmorStandManager feature, TabPlayer owner, String lineName, double yOffset, boolean staticOffset) {
|
||||
return new BukkitArmorStand(this, feature, owner, lineName, yOffset, staticOffset);
|
||||
public boolean isSneaking(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSwimming(TabPlayer player) {
|
||||
Player p = (Player) player.getPlayer();
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14 && p.getPose() == Pose.SWIMMING) return true;
|
||||
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && p.isSwimming();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(TabPlayer player) {
|
||||
return TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && ((Player)player.getPlayer()).isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSleeping(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).isSleeping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getArmorStandType() {
|
||||
return EntityType.ARMOR_STAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).getLocation().getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY(Object entity) {
|
||||
return ((Entity)entity).getLocation().getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).getLocation().getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createDataWatcher(TabPlayer viewer, byte flags, String displayName, boolean nameVisible, boolean markerFlag) {
|
||||
DataWatcher datawatcher = new DataWatcher();
|
||||
datawatcher.getHelper().setEntityFlags(flags);
|
||||
datawatcher.getHelper().setCustomName(displayName, viewer.getVersion());
|
||||
datawatcher.getHelper().setCustomNameVisible(nameVisible);
|
||||
if (markerFlag) datawatcher.getHelper().setArmorStandFlags((byte)16);
|
||||
return datawatcher;
|
||||
}
|
||||
}
|
||||
-1
@@ -46,7 +46,6 @@ public class DataWatcherItem {
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
Object value = VALUE.get(nmsItem);
|
||||
DataWatcherObject object;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.nms;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityDestroyStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityMetadataStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityTeleportStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherHelper;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
@@ -122,13 +122,13 @@ public class BukkitLegacyNMSStorage extends NMSStorage {
|
||||
}
|
||||
|
||||
// Entities
|
||||
PacketPlayOutSpawnEntityLiving.CLASS = getLegacyClass("PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
|
||||
PacketPlayOutEntityTeleport.CLASS = getLegacyClass("PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
|
||||
PacketPlayOutSpawnEntityLivingStorage.CLASS = getLegacyClass("PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
|
||||
PacketPlayOutEntityTeleportStorage.CLASS = getLegacyClass("PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
|
||||
PacketPlayInUseEntity = getLegacyClass("PacketPlayInUseEntity", "Packet7UseEntity");
|
||||
PacketPlayOutEntity = getLegacyClass("PacketPlayOutEntity", "Packet30Entity");
|
||||
PacketPlayOutEntityDestroy.CLASS = getLegacyClass("PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
|
||||
PacketPlayOutEntityDestroyStorage.CLASS = getLegacyClass("PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
|
||||
PacketPlayOutEntityLook = getLegacyClass("PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntityLook", "Packet32EntityLook");
|
||||
PacketPlayOutEntityMetadata.CLASS = getLegacyClass("PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
|
||||
PacketPlayOutEntityMetadataStorage.CLASS = getLegacyClass("PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
|
||||
PacketPlayOutNamedEntitySpawn = getLegacyClass("PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
|
||||
if (minorVersion >= 7) {
|
||||
EnumEntityUseAction = (Class<Enum>) getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
|
||||
|
||||
+18
-14
@@ -1,9 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.nms;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityDestroyStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityMetadataStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityTeleportStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherHelper;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
@@ -34,14 +34,18 @@ public class BukkitModernNMSStorage extends NMSStorage {
|
||||
DataWatcherHelper.DataWatcherSerializer_STRING = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("d").get(null);
|
||||
if (is1_19_3Plus()) {
|
||||
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("g").get(null);
|
||||
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("j").get(null);
|
||||
} else {
|
||||
if (is1_19_4Plus()) {
|
||||
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("k").get(null);
|
||||
} else {
|
||||
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("j").get(null);
|
||||
}
|
||||
} else {
|
||||
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("f").get(null);
|
||||
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("i").get(null);
|
||||
}
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLiving.EntityTypes.getDeclaredField("d").get(null);
|
||||
(PacketPlayOutSpawnEntityLiving.ENTITY_TYPE = PacketPlayOutSpawnEntityLiving.CLASS.getDeclaredField("e")).setAccessible(true);
|
||||
PacketPlayOutSpawnEntityLivingStorage.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLivingStorage.EntityTypes.getDeclaredField("d").get(null);
|
||||
(PacketPlayOutSpawnEntityLivingStorage.ENTITY_TYPE = PacketPlayOutSpawnEntityLivingStorage.CLASS.getDeclaredField("e")).setAccessible(true);
|
||||
DataWatcher.packDirty = DataWatcher.CLASS.getMethod("b");
|
||||
}
|
||||
}
|
||||
@@ -81,21 +85,21 @@ public class BukkitModernNMSStorage extends NMSStorage {
|
||||
|
||||
// Entities
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
|
||||
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
|
||||
} else {
|
||||
PacketPlayOutSpawnEntityLiving.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
|
||||
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
|
||||
}
|
||||
PacketPlayOutEntityTeleport.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
|
||||
PacketPlayOutEntityTeleportStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
|
||||
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity");
|
||||
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$d");
|
||||
PacketPlayOutEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity");
|
||||
PacketPlayOutEntityDestroy.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy");
|
||||
PacketPlayOutEntityDestroyStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy");
|
||||
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
|
||||
PacketPlayOutEntityMetadata.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
|
||||
PacketPlayOutEntityMetadataStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
|
||||
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
|
||||
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
|
||||
PacketPlayOutSpawnEntityLivingStorage.EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
|
||||
+12
-12
@@ -1,9 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.nms;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityDestroyStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityMetadataStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityTeleportStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherHelper;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
@@ -35,8 +35,8 @@ public class MojangModernNMSStorage extends NMSStorage {
|
||||
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("OPTIONAL_COMPONENT").get(null);
|
||||
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("BOOLEAN").get(null);
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLiving.EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
|
||||
(PacketPlayOutSpawnEntityLiving.ENTITY_TYPE = PacketPlayOutSpawnEntityLiving.CLASS.getDeclaredField("type")).setAccessible(true);
|
||||
PacketPlayOutSpawnEntityLivingStorage.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLivingStorage.EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
|
||||
(PacketPlayOutSpawnEntityLivingStorage.ENTITY_TYPE = PacketPlayOutSpawnEntityLivingStorage.CLASS.getDeclaredField("type")).setAccessible(true);
|
||||
DataWatcher.packDirty = DataWatcher.CLASS.getMethod("packDirty");
|
||||
}
|
||||
}
|
||||
@@ -75,21 +75,21 @@ public class MojangModernNMSStorage extends NMSStorage {
|
||||
|
||||
// Entities
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
|
||||
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
|
||||
} else {
|
||||
PacketPlayOutSpawnEntityLiving.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
|
||||
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
|
||||
}
|
||||
PacketPlayOutEntityTeleport.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
|
||||
PacketPlayOutEntityTeleportStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
|
||||
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket");
|
||||
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAction");
|
||||
PacketPlayOutEntity = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket");
|
||||
PacketPlayOutEntityDestroy.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket");
|
||||
PacketPlayOutEntityDestroyStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket");
|
||||
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
|
||||
PacketPlayOutEntityMetadata.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
|
||||
PacketPlayOutEntityMetadataStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
|
||||
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
|
||||
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving.EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
|
||||
PacketPlayOutSpawnEntityLivingStorage.EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
|
||||
+9
-8
@@ -10,10 +10,10 @@ import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.chat.WrappedChatComponent;
|
||||
import me.neznamy.tab.api.util.ComponentCache;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityDestroyStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityMetadataStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutEntityTeleportStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.PacketPlayOutSpawnEntityLivingStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
|
||||
@@ -45,6 +45,7 @@ public abstract class NMSStorage {
|
||||
|
||||
/** Flag determining whether the server version is at least 1.19.3 or not */
|
||||
@Getter private final boolean is1_19_3Plus = ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
@Getter private final boolean is1_19_4Plus = is1_19_3Plus && !serverPackage.equals("v1_19_R2");
|
||||
|
||||
/** Basic universal values */
|
||||
protected Class<?> Packet;
|
||||
@@ -124,10 +125,10 @@ public abstract class NMSStorage {
|
||||
DataWatcher.load(this);
|
||||
DataWatcherItem.load(this);
|
||||
DataWatcherObject.load(this);
|
||||
PacketPlayOutEntityDestroy.load(this);
|
||||
PacketPlayOutEntityMetadata.load(this);
|
||||
PacketPlayOutEntityTeleport.load(this);
|
||||
PacketPlayOutSpawnEntityLiving.load(this);
|
||||
PacketPlayOutEntityDestroyStorage.load(this);
|
||||
PacketPlayOutEntityMetadataStorage.load(this);
|
||||
PacketPlayOutEntityTeleportStorage.load(this);
|
||||
PacketPlayOutSpawnEntityLivingStorage.load(this);
|
||||
PacketPlayOutChatStorage.load(this);
|
||||
PacketPlayOutPlayerListHeaderFooterStorage.load(this);
|
||||
PacketPlayOutPlayerInfoStorage.load(this);
|
||||
|
||||
+6
-20
@@ -1,8 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.packet;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityDestroy;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -10,17 +10,13 @@ import java.lang.reflect.Field;
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
public class PacketPlayOutEntityDestroyStorage implements TabPacket {
|
||||
|
||||
/** NMS Fields */
|
||||
public static Class<?> CLASS;
|
||||
public static Constructor<?> CONSTRUCTOR;
|
||||
public static Field ENTITIES;
|
||||
|
||||
/** Packet's instance fields */
|
||||
private final int[] entities;
|
||||
|
||||
/**
|
||||
* Loads all required Fields and throws Exception if something went wrong
|
||||
*
|
||||
@@ -39,16 +35,6 @@ public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param entities
|
||||
* Destroyed entities
|
||||
*/
|
||||
public PacketPlayOutEntityDestroy(int... entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this class into NMS packet
|
||||
*
|
||||
@@ -56,12 +42,12 @@ public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
* @throws ReflectiveOperationException
|
||||
* If something went wrong
|
||||
*/
|
||||
public Object build() throws ReflectiveOperationException {
|
||||
public static Object build(PacketPlayOutEntityDestroy packet) throws ReflectiveOperationException {
|
||||
if (CONSTRUCTOR.getParameterTypes()[0] != int.class) {
|
||||
return CONSTRUCTOR.newInstance(new Object[]{entities});
|
||||
return CONSTRUCTOR.newInstance(new Object[]{packet.getEntities()});
|
||||
} else {
|
||||
//1.17.0
|
||||
return CONSTRUCTOR.newInstance(entities[0]);
|
||||
return CONSTRUCTOR.newInstance(packet.getEntities()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-13
@@ -1,11 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.packet;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityMetadata;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -14,18 +12,13 @@ import java.util.List;
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityMetadata minecraft packet.
|
||||
*/
|
||||
@AllArgsConstructor @ToString
|
||||
public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
public class PacketPlayOutEntityMetadataStorage implements TabPacket {
|
||||
|
||||
/** NMS Fields */
|
||||
public static Class<?> CLASS;
|
||||
public static Constructor<?> CONSTRUCTOR;
|
||||
public static Field LIST;
|
||||
|
||||
/** Packet's instance fields */
|
||||
private final int entityId;
|
||||
@NonNull private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Loads all required Fields and throws Exception if something went wrong
|
||||
*
|
||||
@@ -50,12 +43,12 @@ public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
* @throws ReflectiveOperationException
|
||||
* If something went wrong
|
||||
*/
|
||||
public Object build() throws ReflectiveOperationException {
|
||||
public static Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
|
||||
if (CONSTRUCTOR.getParameterCount() == 2) {
|
||||
//1.19.3+
|
||||
return CONSTRUCTOR.newInstance(entityId, DataWatcher.packDirty.invoke(dataWatcher.build()));
|
||||
return CONSTRUCTOR.newInstance(packet.getEntityId(), DataWatcher.packDirty.invoke(((DataWatcher)packet.getDataWatcher()).build()));
|
||||
} else {
|
||||
return CONSTRUCTOR.newInstance(entityId, dataWatcher.build(), true);
|
||||
return CONSTRUCTOR.newInstance(packet.getEntityId(), ((DataWatcher)packet.getDataWatcher()).build(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-22
@@ -1,11 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.packet;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import org.bukkit.Location;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityTeleport;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -13,8 +10,7 @@ import java.lang.reflect.Field;
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityTeleport minecraft packet.
|
||||
*/
|
||||
@AllArgsConstructor @ToString
|
||||
public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
public class PacketPlayOutEntityTeleportStorage implements TabPacket {
|
||||
|
||||
/** NMS Fields */
|
||||
public static Class<?> CLASS;
|
||||
@@ -26,10 +22,6 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
public static Field YAW;
|
||||
public static Field PITCH;
|
||||
|
||||
/** Packet's instance fields */
|
||||
private final int entityId;
|
||||
@NonNull private final Location location;
|
||||
|
||||
/**
|
||||
* Loads all required Fields and throws Exception if something went wrong
|
||||
*
|
||||
@@ -65,7 +57,7 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
* @throws ReflectiveOperationException
|
||||
* If something went wrong
|
||||
*/
|
||||
public Object build() throws ReflectiveOperationException {
|
||||
public static Object build(PacketPlayOutEntityTeleport packet) throws ReflectiveOperationException {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
Object nmsPacket;
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
@@ -73,22 +65,22 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
} else {
|
||||
nmsPacket = CONSTRUCTOR.newInstance();
|
||||
}
|
||||
ENTITY_ID.set(nmsPacket, entityId);
|
||||
ENTITY_ID.set(nmsPacket, packet.getEntityId());
|
||||
if (nms.getMinorVersion() >= 9) {
|
||||
X.set(nmsPacket, location.getX());
|
||||
Y.set(nmsPacket, location.getY());
|
||||
Z.set(nmsPacket, location.getZ());
|
||||
X.set(nmsPacket, packet.getX());
|
||||
Y.set(nmsPacket, packet.getY());
|
||||
Z.set(nmsPacket, packet.getZ());
|
||||
} else {
|
||||
X.set(nmsPacket, floor(location.getX()*32));
|
||||
Y.set(nmsPacket, floor(location.getY()*32));
|
||||
Z.set(nmsPacket, floor(location.getZ()*32));
|
||||
X.set(nmsPacket, floor(packet.getX()*32));
|
||||
Y.set(nmsPacket, floor(packet.getY()*32));
|
||||
Z.set(nmsPacket, floor(packet.getZ()*32));
|
||||
}
|
||||
YAW.set(nmsPacket, (byte) (location.getYaw()/360*256));
|
||||
PITCH.set(nmsPacket, (byte) (location.getPitch()/360*256));
|
||||
YAW.set(nmsPacket, (byte) (packet.getYaw()/360*256));
|
||||
PITCH.set(nmsPacket, (byte) (packet.getPitch()/360*256));
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
private int floor(double paramDouble) {
|
||||
private static int floor(double paramDouble) {
|
||||
int i = (int)paramDouble;
|
||||
return paramDouble < i ? i - 1 : i;
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.packet;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
@@ -175,8 +176,9 @@ public class PacketPlayOutPlayerInfoStorage {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_getProfile.invoke(nmsData);
|
||||
Object nmsComponent = PlayerInfoData_getDisplayName.invoke(nmsData);
|
||||
Skin skin = null;
|
||||
if (!profile.getProperties().get("textures").isEmpty()) {
|
||||
Property pr = profile.getProperties().get("textures").iterator().next();
|
||||
Collection<Property> textures = profile.getProperties().get("textures");
|
||||
Property pr = Iterables.getFirst(textures, null);
|
||||
if (pr != null) {
|
||||
skin = new Skin(pr.getValue(), pr.getSignature());
|
||||
}
|
||||
boolean listed = true;
|
||||
|
||||
+17
-29
@@ -1,12 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage.packet;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.NMSStorage;
|
||||
import org.bukkit.Location;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutSpawnEntityLiving;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -17,8 +14,7 @@ import java.util.UUID;
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutSpawnEntityLiving minecraft packet.
|
||||
*/
|
||||
@AllArgsConstructor @ToString
|
||||
public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
public class PacketPlayOutSpawnEntityLivingStorage implements TabPacket {
|
||||
|
||||
/** NMS Fields */
|
||||
public static Class<?> CLASS;
|
||||
@@ -37,13 +33,6 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
|
||||
/** Entity types */
|
||||
public static final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
|
||||
|
||||
/** Packet's instance fields */
|
||||
private final int entityId;
|
||||
@NonNull private final UUID uniqueId;
|
||||
@NonNull private final EntityType entityType;
|
||||
@NonNull private final Location location;
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Loads all required Fields and throws Exception if something went wrong
|
||||
@@ -108,7 +97,7 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
* @throws ReflectiveOperationException
|
||||
* If something went wrong
|
||||
*/
|
||||
public Object build() throws ReflectiveOperationException {
|
||||
public static Object build(PacketPlayOutSpawnEntityLiving packet) throws ReflectiveOperationException {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
Object nmsPacket;
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
@@ -116,32 +105,31 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
} else {
|
||||
nmsPacket = CONSTRUCTOR.newInstance();
|
||||
}
|
||||
ENTITY_ID.set(nmsPacket, entityId);
|
||||
YAW.set(nmsPacket, (byte)(location.getYaw() * 256.0f / 360.0f));
|
||||
PITCH.set(nmsPacket, (byte)(location.getPitch() * 256.0f / 360.0f));
|
||||
ENTITY_ID.set(nmsPacket, packet.getEntityId());
|
||||
YAW.set(nmsPacket, (byte)(packet.getYaw() * 256.0f / 360.0f));
|
||||
PITCH.set(nmsPacket, (byte)(packet.getPitch() * 256.0f / 360.0f));
|
||||
if (nms.getMinorVersion() <= 14) {
|
||||
DATA_WATCHER.set(nmsPacket, dataWatcher.build());
|
||||
DATA_WATCHER.set(nmsPacket, ((DataWatcher)packet.getDataWatcher()).build());
|
||||
}
|
||||
if (nms.getMinorVersion() >= 9) {
|
||||
UUID.set(nmsPacket, uniqueId);
|
||||
X.set(nmsPacket, location.getX());
|
||||
Y.set(nmsPacket, location.getY());
|
||||
Z.set(nmsPacket, location.getZ());
|
||||
UUID.set(nmsPacket, packet.getUniqueId());
|
||||
X.set(nmsPacket, packet.getX());
|
||||
Y.set(nmsPacket, packet.getY());
|
||||
Z.set(nmsPacket, packet.getZ());
|
||||
} else {
|
||||
X.set(nmsPacket, floor(location.getX()*32));
|
||||
Y.set(nmsPacket, floor(location.getY()*32));
|
||||
Z.set(nmsPacket, floor(location.getZ()*32));
|
||||
X.set(nmsPacket, floor(packet.getX()*32));
|
||||
Y.set(nmsPacket, floor(packet.getY()*32));
|
||||
Z.set(nmsPacket, floor(packet.getZ()*32));
|
||||
}
|
||||
int id = entityIds.get(entityType);
|
||||
if (nms.getMinorVersion() >= 19) {
|
||||
ENTITY_TYPE.set(nmsPacket, EntityTypes_ARMOR_STAND); // :(
|
||||
} else {
|
||||
ENTITY_TYPE.set(nmsPacket, id);
|
||||
ENTITY_TYPE.set(nmsPacket, entityIds.get((EntityType) packet.getEntityType()));
|
||||
}
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
private int floor(double paramDouble) {
|
||||
private static int floor(double paramDouble) {
|
||||
int i = (int)paramDouble;
|
||||
return paramDouble < i ? i - 1 : i;
|
||||
}
|
||||
@@ -3,4 +3,5 @@ dependencies {
|
||||
implementation(libs.bstats.bungeecord)
|
||||
compileOnly(libs.bungeecord)
|
||||
compileOnly(libs.redisBungee)
|
||||
compileOnly("io.netty:netty-transport-native-epoll:4.1.84.Final") // Fixing compilation with 1.19.4 update
|
||||
}
|
||||
+33
-6
@@ -2,19 +2,22 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import java.lang.reflect.Field;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
|
||||
import me.neznamy.tab.shared.features.redis.RedisPlayer;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
|
||||
@@ -30,13 +33,37 @@ import java.util.function.Supplier;
|
||||
* from overriding it.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BungeePipelineInjector extends PipelineInjector {
|
||||
public class BungeePipelineInjector extends NettyPipelineInjector {
|
||||
|
||||
private static Field wrapperField;
|
||||
|
||||
static {
|
||||
try {
|
||||
(wrapperField = InitialHandler.class.getDeclaredField("ch")).setAccessible(true);
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/** Packets used by the plugin that must be deserialized and BungeeCord does not do it automatically */
|
||||
private final Class<? extends DefinedPacket>[] extraPacketClasses = new Class[]{Team.class, ScoreboardDisplay.class, ScoreboardObjective.class};
|
||||
private final Supplier<DefinedPacket>[] extraPacketSuppliers = new Supplier[]{Team::new, ScoreboardDisplay::new, ScoreboardObjective::new};
|
||||
|
||||
@Getter private final Function<TabPlayer, ChannelDuplexHandler> channelFunction = BungeeChannelDuplexHandler::new;
|
||||
@Override
|
||||
public Function<TabPlayer, ChannelDuplexHandler> getChannelFunction() {
|
||||
return byteBufDeserialization ? DeserializableBungeeChannelDuplexHandler::new : BungeeChannelDuplexHandler::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Channel getChannel(TabPlayer player) {
|
||||
final BungeeTabPlayer bungee = (BungeeTabPlayer) player;
|
||||
try {
|
||||
return ((ChannelWrapper) wrapperField.get(bungee.getPlayer().getPendingConnection())).getHandle();
|
||||
} catch (final IllegalAccessException exception) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + bungee.getPlayer().getName(), exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance of the feature
|
||||
@@ -84,12 +111,12 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (Exception | NoSuchMethodError e) {
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when analyzing packets for player " + player.getName() + " with client version " + player.getVersion().getFriendlyName(), e);
|
||||
}
|
||||
try {
|
||||
super.write(context, packet, channelPromise);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to forward packet %s to %s", packet.getClass().getSimpleName(), player.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
@@ -26,7 +25,6 @@ import java.util.UUID;
|
||||
public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
/** Inaccessible bungee internals */
|
||||
private static Field wrapperField;
|
||||
private static Object directionData;
|
||||
private static Method getId;
|
||||
|
||||
@@ -35,10 +33,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
Field f = Protocol.class.getDeclaredField("TO_CLIENT");
|
||||
f.setAccessible(true);
|
||||
directionData = f.get(Protocol.GAME);
|
||||
getId = directionData.getClass().getDeclaredMethod("getId", Class.class, int.class);
|
||||
getId.setAccessible(true);
|
||||
wrapperField = InitialHandler.class.getDeclaredField("ch");
|
||||
wrapperField.setAccessible(true);
|
||||
(getId = directionData.getClass().getDeclaredMethod("getId", Class.class, int.class)).setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
|
||||
}
|
||||
@@ -51,12 +46,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
* BungeeCord player
|
||||
*/
|
||||
public BungeeTabPlayer(ProxiedPlayer p) {
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1, true);
|
||||
try {
|
||||
channel = ((ChannelWrapper)wrapperField.get(getPlayer().getPendingConnection())).getHandle();
|
||||
} catch (IllegalAccessException e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + getPlayer().getName(), e);
|
||||
}
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,6 +140,5 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
if (getPlayer().getServer() == null) return;
|
||||
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
|
||||
TAB.getInstance().getCPUManager().packetSent("Plugin Message");
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
@@ -12,14 +13,17 @@ import net.md_5.bungee.event.EventHandler;
|
||||
/**
|
||||
* Redis implementation for BungeeCord
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class RedisBungeeSupport extends RedisSupport implements Listener {
|
||||
|
||||
/**
|
||||
* Constructs new instance, registers listeners and overrides placeholders
|
||||
*/
|
||||
public RedisBungeeSupport(Plugin plugin) {
|
||||
/** Plugin reference for registering listener */
|
||||
private final Plugin plugin;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(plugin, this);
|
||||
RedisBungeeAPI.getRedisBungeeApi().registerPubSubChannels(TabConstants.REDIS_CHANNEL_NAME);
|
||||
super.load();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
||||
@@ -22,8 +22,8 @@ bukkit = "1.16.5-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.19-R0.1-SNAPSHOT"
|
||||
krypton = "a81b4baaed" # Krypton uses commit-based versioning
|
||||
velocity = "3.1.2-SNAPSHOT"
|
||||
sponge7 = "7.4.0"
|
||||
sponge8 = "8.1.0"
|
||||
sponge7 = "7.0.0"
|
||||
sponge8 = "8.0.0"
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = "2.11.2"
|
||||
|
||||
@@ -6,27 +6,38 @@ import me.neznamy.tab.api.chat.IChatBaseComponent
|
||||
import me.neznamy.tab.api.protocol.*
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData
|
||||
import me.neznamy.tab.api.util.BiFunctionWithException
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityMetadata
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutSpawnEntityLiving
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
|
||||
import org.kryptonmc.api.auth.GameProfile
|
||||
import org.kryptonmc.api.auth.ProfileProperty
|
||||
import org.kryptonmc.api.world.GameMode
|
||||
import org.kryptonmc.krypton.adventure.KryptonAdventure
|
||||
import org.kryptonmc.krypton.entity.KryptonEntityType
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataHolder
|
||||
import org.kryptonmc.krypton.entity.player.PlayerPublicKey
|
||||
import org.kryptonmc.krypton.network.chat.RemoteChatSession
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutDisplayObjective
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoRemove
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoUpdate
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateObjectives
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateScore
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateTeams
|
||||
import java.util.EnumSet
|
||||
import org.kryptonmc.krypton.packet.out.play.*
|
||||
import java.util.*
|
||||
|
||||
// All build functions that just return the packet parameter will be passed through to be handled in KryptonTabPlayer.
|
||||
object KryptonPacketBuilder : PacketBuilder() {
|
||||
|
||||
private val GAME_MODES = GameMode.values()
|
||||
|
||||
init {
|
||||
buildMap[PacketPlayOutEntityMetadata::class.java] =
|
||||
BiFunctionWithException { packet: TabPacket, _: ProtocolVersion? ->
|
||||
build(packet as PacketPlayOutEntityMetadata)
|
||||
}
|
||||
buildMap[PacketPlayOutSpawnEntityLiving::class.java] =
|
||||
BiFunctionWithException { packet: TabPacket, _: ProtocolVersion? ->
|
||||
build(packet as PacketPlayOutSpawnEntityLiving)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toComponent(text: String?, clientVersion: ProtocolVersion): Component {
|
||||
if (text.isNullOrEmpty()) return Component.empty()
|
||||
@@ -93,6 +104,15 @@ object KryptonPacketBuilder : PacketBuilder() {
|
||||
return PacketOutUpdateTeams(packet.name, action, createParameters(packet, clientVersion), players)
|
||||
}
|
||||
|
||||
private fun build(packet: PacketPlayOutEntityMetadata): Any {
|
||||
return PacketOutSetEntityMetadata(packet.entityId, (packet.dataWatcher as MetadataHolder).collectAll())
|
||||
}
|
||||
|
||||
private fun build(packet: PacketPlayOutSpawnEntityLiving): Any {
|
||||
return PacketOutSpawnEntity(packet.entityId, packet.uniqueId, packet.entityType as KryptonEntityType<*>,
|
||||
packet.x, packet.y, packet.z, 0, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
private fun createParameters(packet: PacketPlayOutScoreboardTeam, clientVersion: ProtocolVersion): PacketOutUpdateTeams.Parameters? {
|
||||
if (packet.action != 0 && packet.action != 2) return null
|
||||
var prefix = packet.playerPrefix
|
||||
|
||||
+11
-4
@@ -1,26 +1,33 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import io.netty.channel.Channel
|
||||
import io.netty.channel.ChannelDuplexHandler
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.channel.ChannelPromise
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.features.PipelineInjector
|
||||
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector
|
||||
import org.kryptonmc.krypton.network.NettyConnection
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoRemove
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoUpdate
|
||||
import java.util.function.Function
|
||||
|
||||
class KryptonPipelineInjector : PipelineInjector("handler") {
|
||||
class KryptonPipelineInjector : NettyPipelineInjector("handler") {
|
||||
|
||||
override fun getChannelFunction(): Function<TabPlayer, ChannelDuplexHandler> = Function(::KryptonChannelDuplexHandler)
|
||||
|
||||
override fun getChannel(player: TabPlayer?): Channel {
|
||||
val connection = (player as KryptonTabPlayer).connection()
|
||||
return NettyConnection::class.java.getDeclaredField("channel").apply { isAccessible = true }.get(connection) as Channel
|
||||
}
|
||||
|
||||
inner class KryptonChannelDuplexHandler(private val player: TabPlayer) : ChannelDuplexHandler() {
|
||||
|
||||
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
|
||||
try {
|
||||
if (TAB.getInstance().featureManager.onPacketReceive(player, msg)) return
|
||||
super.channelRead(ctx, msg)
|
||||
} catch (exception: Exception) {
|
||||
} catch (exception: Throwable) {
|
||||
TAB.getInstance().errorManager.printError("An error occurred when reading packets", exception)
|
||||
}
|
||||
}
|
||||
@@ -37,7 +44,7 @@ class KryptonPipelineInjector : PipelineInjector("handler") {
|
||||
}
|
||||
try {
|
||||
super.write(ctx, msg, promise)
|
||||
} catch (exception: Exception) {
|
||||
} catch (exception: Throwable) {
|
||||
TAB.getInstance().errorManager.printError("Failed to forward packet ${msg.javaClass.simpleName} to ${player.name}", exception)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import me.lucko.spark.api.Spark
|
||||
import me.lucko.spark.api.statistic.StatisticWindow.*
|
||||
import me.neznamy.tab.api.TabConstants
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
@@ -11,8 +12,8 @@ class KryptonPlaceholderRegistry(private val plugin: Main) : UniversalPlaceholde
|
||||
|
||||
override fun registerPlaceholders(manager: PlaceholderManager) {
|
||||
// Built-in stuff
|
||||
manager.registerPlayerPlaceholder("%displayname%", 500) { (it.player as Player).displayName }
|
||||
manager.registerPlayerPlaceholder("%health%", 100) { ceil((it.player as Player).health) }
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500) { (it.player as Player).displayName }
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100) { ceil((it.player as Player).health) }
|
||||
|
||||
// Spark placeholders, registered if Spark is present
|
||||
registerSparkPlaceholders(manager)
|
||||
|
||||
@@ -5,9 +5,10 @@ import me.neznamy.tab.api.protocol.PacketBuilder
|
||||
import me.neznamy.tab.platforms.krypton.features.unlimitedtags.KryptonNameTagX
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform
|
||||
import me.neznamy.tab.shared.features.PipelineInjector
|
||||
import me.neznamy.tab.shared.features.TabExpansion
|
||||
import me.neznamy.tab.shared.features.injection.PipelineInjector
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag
|
||||
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
|
||||
@@ -44,8 +45,8 @@ class KryptonPlatform(private val plugin: Main) : BackendPlatform() {
|
||||
return KryptonNameTagX(plugin)
|
||||
}
|
||||
|
||||
override fun getTabExpansion(): TabExpansion? {
|
||||
return null
|
||||
override fun getTabExpansion(): TabExpansion {
|
||||
return EmptyTabExpansion()
|
||||
}
|
||||
|
||||
override fun getPetFix(): TabFeature? {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import io.netty.channel.Channel
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action
|
||||
@@ -31,10 +30,7 @@ class KryptonTabPlayer(
|
||||
private val delegate = delegate as KryptonPlayer
|
||||
private val bossBars = mutableMapOf<UUID, BossBar>()
|
||||
|
||||
init {
|
||||
val connection = this.delegate.connection
|
||||
channel = NettyConnection::class.java.getDeclaredField("channel").apply { isAccessible = true }.get(connection) as Channel
|
||||
}
|
||||
fun connection(): NettyConnection = delegate.connection
|
||||
|
||||
override fun hasPermission(permission: String): Boolean = delegate.hasPermission(permission)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.nio.file.Path
|
||||
TabConstants.PLUGIN_NAME,
|
||||
TabConstants.PLUGIN_VERSION,
|
||||
TabConstants.PLUGIN_DESCRIPTION,
|
||||
["NEZNAMY", "BomBardyGamer"],
|
||||
[TabConstants.PLUGIN_AUTHOR, "BomBardyGamer"],
|
||||
[
|
||||
Dependency("luckperms", true),
|
||||
Dependency("viaversion", true)
|
||||
@@ -77,7 +77,7 @@ class Main @Inject constructor(
|
||||
|
||||
fun getProtocolVersion(player: Player): Int {
|
||||
if (server.pluginManager.isLoaded(TabConstants.Plugin.VIAVERSION.lowercase())) {
|
||||
return TAB.getInstance().platform.getProtocolVersionVia(player.uuid, player.profile.name, 0)
|
||||
return ProtocolVersion.getPlayerVersionVia(player.uuid, player.profile.name)
|
||||
}
|
||||
return TAB.getInstance().serverVersion.networkId
|
||||
}
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.platforms.krypton.KryptonPacketBuilder
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager
|
||||
import org.kryptonmc.krypton.entity.KryptonEntityTypes
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataHolder
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataKeys
|
||||
import org.kryptonmc.krypton.entity.player.KryptonPlayer
|
||||
import org.kryptonmc.krypton.packet.Packet
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutRemoveEntities
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSetEntityMetadata
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnEntity
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutTeleportEntity
|
||||
|
||||
class KryptonArmorStand(
|
||||
manager: BackendArmorStandManager,
|
||||
nameTagX: KryptonNameTagX,
|
||||
owner: TabPlayer,
|
||||
propertyName: String,
|
||||
yOffset: Double,
|
||||
staticOffset: Boolean
|
||||
) : BackendArmorStand(nameTagX, manager, owner, propertyName, yOffset, staticOffset) {
|
||||
|
||||
private val player = owner.player as KryptonPlayer
|
||||
private val destroyPacket = PacketOutRemoveEntities(intArrayOf(entityId))
|
||||
|
||||
override fun spawn(viewer: TabPlayer) {
|
||||
getSpawnPackets(viewer).forEach { viewer.sendPacket(it) }
|
||||
}
|
||||
|
||||
override fun destroy(viewer: TabPlayer) {
|
||||
viewer.sendPacket(destroyPacket)
|
||||
}
|
||||
|
||||
private fun getArmorStandYFor(viewer: TabPlayer): Double {
|
||||
var y = player.position.y
|
||||
if (player.isSwimming || player.isGliding) y -= 1.22
|
||||
y += getYAdd(false, sneaking, viewer)
|
||||
return y
|
||||
}
|
||||
|
||||
override fun updateMetadata(viewer: TabPlayer) {
|
||||
viewer.sendPacket(PacketOutSetEntityMetadata(entityId, createMetadata(property.getFormat(viewer), viewer).collectAll()))
|
||||
}
|
||||
|
||||
override fun sendTeleportPacket(viewer: TabPlayer) {
|
||||
viewer.sendPacket(PacketOutTeleportEntity(entityId, player.position.x, getArmorStandYFor(viewer), player.position.z, 0, 0, false))
|
||||
}
|
||||
|
||||
private fun getSpawnPackets(viewer: TabPlayer): Array<Packet> {
|
||||
visible = calculateVisibility()
|
||||
val data = createMetadata(property.getFormat(viewer), viewer)
|
||||
return arrayOf(
|
||||
PacketOutSpawnEntity(entityId, uuid, KryptonEntityTypes.ARMOR_STAND, player.position.x, getArmorStandYFor(viewer),
|
||||
player.position.z, 0, 0, 0, 0, 0, 0, 0),
|
||||
PacketOutSetEntityMetadata(entityId, data.collectAll())
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMetadata(displayName: String, viewer: TabPlayer): MetadataHolder {
|
||||
val viewerPlayer = viewer.player as KryptonPlayer
|
||||
val holder = MetadataHolder(viewerPlayer).apply {
|
||||
define(MetadataKeys.Entity.FLAGS, 0)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME, null)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, false)
|
||||
}
|
||||
holder.set(MetadataKeys.Entity.FLAGS, (if (sneaking) 34 else 32).toByte())
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME, KryptonPacketBuilder.toComponent(displayName, viewer.version))
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, !shouldBeInvisibleFor(viewer, displayName) && visible)
|
||||
if (viewer.version.minorVersion > 8 || manager.isMarkerFor18x) {
|
||||
holder.define(MetadataKeys.ArmorStand.FLAGS, 16.toByte())
|
||||
}
|
||||
return holder
|
||||
}
|
||||
}
|
||||
+51
-5
@@ -2,14 +2,17 @@ package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabConstants
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.platforms.krypton.KryptonPacketBuilder
|
||||
import me.neznamy.tab.platforms.krypton.Main
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX
|
||||
import org.kryptonmc.api.entity.Entity
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.event.EventNode
|
||||
import org.kryptonmc.krypton.entity.KryptonEntityTypes
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataHolder
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataKeys
|
||||
import org.kryptonmc.krypton.entity.player.KryptonPlayer
|
||||
import org.kryptonmc.krypton.packet.EntityPacket
|
||||
import org.kryptonmc.krypton.packet.MovementPacket
|
||||
import org.kryptonmc.krypton.packet.`in`.play.PacketInInteract
|
||||
@@ -21,10 +24,11 @@ class KryptonNameTagX(private val plugin: Main) : BackendNameTagX() {
|
||||
|
||||
private val eventListener = EventListener(this)
|
||||
|
||||
init {
|
||||
override fun load() {
|
||||
val eventNode = EventNode.all("tab_nametagx")
|
||||
plugin.eventNode.addChild(eventNode)
|
||||
eventNode.registerListeners(eventListener)
|
||||
super.load()
|
||||
}
|
||||
|
||||
override fun onPacketReceive(sender: TabPlayer, packet: Any): Boolean {
|
||||
@@ -97,8 +101,50 @@ class KryptonNameTagX(private val plugin: Main) : BackendNameTagX() {
|
||||
return (entity as Entity).type.key().value()
|
||||
}
|
||||
|
||||
override fun createArmorStand(asm: BackendArmorStandManager, owner: TabPlayer, lineName: String, yOffset: Double, staticOffset: Boolean): BackendArmorStand {
|
||||
return KryptonArmorStand(asm, this, owner, lineName, yOffset, staticOffset)
|
||||
override fun isSneaking(player: TabPlayer): Boolean {
|
||||
return (player as Player).isSneaking
|
||||
}
|
||||
|
||||
override fun isSwimming(player: TabPlayer?): Boolean {
|
||||
return (player as Player).isSwimming
|
||||
}
|
||||
|
||||
override fun isGliding(player: TabPlayer?): Boolean {
|
||||
return (player as Player).isGliding
|
||||
}
|
||||
|
||||
override fun isSleeping(player: TabPlayer?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getArmorStandType(): Any {
|
||||
return KryptonEntityTypes.ARMOR_STAND
|
||||
}
|
||||
|
||||
override fun getX(player: TabPlayer): Double {
|
||||
return (player as Player).position.x
|
||||
}
|
||||
|
||||
override fun getY(entity: Any): Double {
|
||||
return (entity as Entity).position.y
|
||||
}
|
||||
|
||||
override fun getZ(player: TabPlayer): Double {
|
||||
return (player as Player).position.z
|
||||
}
|
||||
|
||||
override fun createDataWatcher(viewer: TabPlayer, flags: Byte, displayName: String, nameVisible: Boolean, markerFlag: Boolean): Any {
|
||||
val viewerPlayer = viewer.player as KryptonPlayer
|
||||
val holder = MetadataHolder(viewerPlayer).apply {
|
||||
define(MetadataKeys.Entity.FLAGS, 0)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME, null)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, false)
|
||||
}
|
||||
holder.set(MetadataKeys.Entity.FLAGS, flags)
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME, KryptonPacketBuilder.toComponent(displayName, viewer.version))
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, nameVisible)
|
||||
if (markerFlag) holder.define(MetadataKeys.ArmorStand.FLAGS, 16.toByte())
|
||||
return holder
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -4,9 +4,9 @@ dependencies {
|
||||
exclude("com.google.guava", "guava")
|
||||
exclude("org.checkerframework", "checker-qual")
|
||||
}
|
||||
compileOnlyApi(libs.netty)
|
||||
compileOnlyApi(libs.luckperms)
|
||||
compileOnlyApi(libs.guava)
|
||||
compileOnlyApi(libs.floodgate)
|
||||
compileOnlyApi(libs.slf4j)
|
||||
compileOnlyApi(libs.via)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.RelationalPlaceholderImpl;
|
||||
|
||||
/**
|
||||
@@ -120,8 +120,8 @@ public class DynamicText implements Property {
|
||||
}
|
||||
lastReplacedValue = rawFormattedValue;
|
||||
update();
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null && name != null) {
|
||||
if (name != null) {
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
expansion.setPropertyValue(owner, name, lastReplacedValue);
|
||||
expansion.setRawPropertyValue(owner, name, getCurrentRawValue());
|
||||
}
|
||||
@@ -193,9 +193,8 @@ public class DynamicText implements Property {
|
||||
string = EnumChatFormat.color(string);
|
||||
if (!lastReplacedValue.equals(string)) {
|
||||
lastReplacedValue = string;
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null && name != null) {
|
||||
expansion.setPropertyValue(owner, name, lastReplacedValue);
|
||||
if (name != null) {
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setPropertyValue(owner, name, lastReplacedValue);
|
||||
}
|
||||
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@@ -7,30 +12,35 @@ import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An error assistant to print internal errors into error file
|
||||
* and warn user about misconfiguration
|
||||
*/
|
||||
|
||||
public class ErrorManager {
|
||||
|
||||
/** Date format used in error messages */
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy - HH:mm:ss - ");
|
||||
|
||||
/** errors.log file for internal plugin errors */
|
||||
private final File errorLog = new File(TAB.getInstance().getDataFolder(), "errors.log");
|
||||
private final File errorLog;
|
||||
|
||||
/** anti-override.log file when some plugin or server itself attempts to override the plugin */
|
||||
@Getter private final File antiOverrideLog = new File(TAB.getInstance().getDataFolder(), "anti-override.log");
|
||||
@Getter private final File antiOverrideLog;
|
||||
|
||||
/** placeholder-errors.log file for errors thrown by placeholders */
|
||||
private final File placeholderErrorLog = new File(TAB.getInstance().getDataFolder(), "placeholder-errors.log");
|
||||
private final File placeholderErrorLog;
|
||||
|
||||
public ErrorManager(TAB tab) {
|
||||
errorLog = new File(tab.getDataFolder(), "errors.log");
|
||||
antiOverrideLog = new File(tab.getDataFolder(), "anti-override.log");
|
||||
placeholderErrorLog = new File(tab.getDataFolder(), "placeholder-errors.log");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints error message into errors.log file
|
||||
|
||||
@@ -39,7 +39,12 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
* This function is called on plugin startup.
|
||||
*/
|
||||
public void load() {
|
||||
for (TabFeature f : values) f.load();
|
||||
for (TabFeature f : values) {
|
||||
if (!f.overridesMethod("load")) continue;
|
||||
long time = System.currentTimeMillis();
|
||||
f.load();
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed load in " + (System.currentTimeMillis()-time) + "ms");
|
||||
}
|
||||
if (TAB.getInstance().getConfiguration().getUsers() instanceof MySQLUserConfiguration) {
|
||||
MySQLUserConfiguration users = (MySQLUserConfiguration) TAB.getInstance().getConfiguration().getUsers();
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) users.load(p);
|
||||
@@ -51,7 +56,13 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
* This function is called on plugin disable.
|
||||
*/
|
||||
public void unload() {
|
||||
for (TabFeature f : values) f.unload();
|
||||
for (TabFeature f : values) {
|
||||
if (!f.overridesMethod("unload")) continue;
|
||||
long time = System.currentTimeMillis();
|
||||
f.unload();
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed unload in " + (System.currentTimeMillis()-time) + "ms");
|
||||
|
||||
}
|
||||
if (TAB.getInstance().getPlatform() instanceof ProxyPlatform) {
|
||||
PluginMessageHandler plm = ((ProxyPlatform) TAB.getInstance().getPlatform()).getPluginMessageHandler();
|
||||
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
|
||||
@@ -310,6 +321,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
|
||||
@Override
|
||||
public void registerFeature(String featureName, TabFeature featureHandler) {
|
||||
if (featureName == null || featureHandler == null) return;
|
||||
features.put(featureName, featureHandler);
|
||||
values = features.values().toArray(new TabFeature[0]);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ public class GroupManager extends TabFeature {
|
||||
/** List of group permissions to iterate through if {@link #groupsByPermissions} is {@code true} */
|
||||
private final List<String> primaryGroupFindingList = TAB.getInstance().getConfiguration().getConfig().getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"));
|
||||
|
||||
@Getter private final String featureName = "Permission group refreshing";
|
||||
|
||||
/**
|
||||
* Constructs new instance with given permission plugin, loads
|
||||
* event listeners and registers group placeholder.
|
||||
@@ -31,7 +33,6 @@ public class GroupManager extends TabFeature {
|
||||
* Detected permission plugin
|
||||
*/
|
||||
public GroupManager(PermissionPlugin plugin) {
|
||||
super("Permission group refreshing", null);
|
||||
this.plugin = plugin;
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(1000, this, TabConstants.CpuUsageCategory.GROUP_REFRESHING, () -> {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.FeatureManager;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
@@ -10,17 +11,18 @@ import me.neznamy.tab.shared.config.Configs;
|
||||
import me.neznamy.tab.shared.features.*;
|
||||
import me.neznamy.tab.shared.features.alignedplayerlist.AlignedPlayerList;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.globalplayerlist.GlobalPlayerList;
|
||||
import me.neznamy.tab.shared.features.injection.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An interface with methods that are called in universal code,
|
||||
* but require platform-specific API calls
|
||||
@@ -49,34 +51,15 @@ public abstract class Platform {
|
||||
public void loadFeatures() {
|
||||
Configs configuration = TAB.getInstance().getConfiguration();
|
||||
FeatureManager featureManager = TAB.getInstance().getFeatureManager();
|
||||
|
||||
if (configuration.isPipelineInjection() && TAB.getInstance().getServerVersion().getMinorVersion() >= 8) {
|
||||
PipelineInjector inj = getPipelineInjector();
|
||||
if (inj != null) featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, inj);
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)
|
||||
&& TAB.getInstance().getServerVersion().getMinorVersion() >= 8) {
|
||||
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, getUnlimitedNametags());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
|
||||
}
|
||||
}
|
||||
int minorVersion = TAB.getInstance().getServerVersion().getMinorVersion();
|
||||
|
||||
if (configuration.getConfig().getBoolean("bossbar.enabled", false)) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, getLegacyBossBar());
|
||||
}
|
||||
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR,
|
||||
minorVersion >= 9 ? new BossBarManagerImpl() : getLegacyBossBar());
|
||||
}
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9 &&
|
||||
configuration.getConfig().getBoolean("fix-pet-names.enabled", false)) {
|
||||
TabFeature petFix = getPetFix();
|
||||
if (petFix != null) featureManager.registerFeature(TabConstants.Feature.PET_FIX, getPetFix());
|
||||
if (minorVersion >= 9 && configuration.getConfig().getBoolean("fix-pet-names.enabled", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PET_FIX, getPetFix());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("header-footer.enabled", true))
|
||||
@@ -85,7 +68,42 @@ public abstract class Platform {
|
||||
if (configuration.isRemoveGhostPlayers())
|
||||
featureManager.registerFeature(TabConstants.Feature.GHOST_PLAYER_FIX, new GhostPlayerFix());
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
|
||||
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
|
||||
|
||||
if (configuration.isPipelineInjection()) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, getPipelineInjector());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
|
||||
|
||||
if (configuration.getConfig().getBoolean("per-world-playerlist.enabled", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, getPerWorldPlayerlist());
|
||||
}
|
||||
|
||||
// No requirements, but due to chicken vs egg, the feature uses NameTags, Layout and RedisBungee
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true) ||
|
||||
configuration.getLayout().getBoolean("enabled", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
}
|
||||
|
||||
// Must be loaded after: Sorting
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && minorVersion >= 8) {
|
||||
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, getUnlimitedNametags());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
|
||||
}
|
||||
}
|
||||
|
||||
// Must be loaded after: Sorting
|
||||
if (minorVersion >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
|
||||
}
|
||||
|
||||
// Must be loaded after: Layout
|
||||
if (minorVersion >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
|
||||
if (configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new AlignedPlayerList());
|
||||
} else {
|
||||
@@ -93,46 +111,28 @@ public abstract class Platform {
|
||||
}
|
||||
}
|
||||
|
||||
// Must be loaded after: PlayerList
|
||||
if (configuration.getConfig().getBoolean("global-playerlist.enabled", false) &&
|
||||
TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
|
||||
featureManager.registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST, new GlobalPlayerList());
|
||||
}
|
||||
|
||||
// Must be loaded after: Layout
|
||||
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
|
||||
|
||||
// Must be loaded after: Global PlayerList, PlayerList, NameTags
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, getRedisSupport());
|
||||
|
||||
// Must be loaded after: RedisBungee
|
||||
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
|
||||
|
||||
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
|
||||
|
||||
// Must be loaded after: RedisBungee
|
||||
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
|
||||
|
||||
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
|
||||
if (TAB.getInstance().getTeamManager() == null) {
|
||||
//sorting is disabled, but layout needs team names
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
}
|
||||
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("placeholders.register-tab-expansion", false)) {
|
||||
TAB.getInstance().getPlaceholderManager().setTabExpansion(getTabExpansion());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("global-playerlist.enabled", false)) {
|
||||
TabFeature global = getGlobalPlayerlist();
|
||||
if (global != null) featureManager.registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST, getGlobalPlayerlist());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("per-world-playerlist.enabled", false)) {
|
||||
TabFeature pwp = getPerWorldPlayerlist();
|
||||
if (pwp != null) featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, pwp);
|
||||
}
|
||||
|
||||
RedisSupport redis = getRedisSupport();
|
||||
if (redis != null) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, redis);
|
||||
|
||||
// Must be loaded after: NameTags, BelowName, YellowNumber, RedisBungee
|
||||
featureManager.registerFeature(TabConstants.Feature.NICK_COMPATIBILITY, new NickCompatibility());
|
||||
}
|
||||
|
||||
@@ -140,38 +140,6 @@ public abstract class Platform {
|
||||
return new BossBarManagerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns protocol version of requested player using ViaVersion
|
||||
*
|
||||
* @param player
|
||||
* UUID of player to get protocol version of
|
||||
* @param playerName
|
||||
* Name of the player
|
||||
* @return protocol version of the player using ViaVersion
|
||||
*/
|
||||
public int getProtocolVersionVia(UUID player, String playerName, int retryLevel) {
|
||||
try {
|
||||
if (retryLevel == 10) {
|
||||
TAB.getInstance().debug("Failed to get protocol version of " + playerName + " after 10 retries");
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
int version = Via.getAPI().getPlayerVersion(player);
|
||||
if (version == -1) {
|
||||
Thread.sleep(5);
|
||||
return getProtocolVersionVia(player, playerName, retryLevel + 1);
|
||||
}
|
||||
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
|
||||
return version;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return -1;
|
||||
} catch (Exception | LinkageError e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s",
|
||||
playerName, getPluginVersion(TabConstants.Plugin.VIAVERSION)), e);
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link me.neznamy.tab.api.placeholder.Placeholder}
|
||||
* to handle this unknown placeholder (typically a PAPI placeholder)
|
||||
@@ -205,12 +173,10 @@ public abstract class Platform {
|
||||
|
||||
public abstract NameTag getUnlimitedNametags();
|
||||
|
||||
public abstract TabExpansion getTabExpansion();
|
||||
public abstract @NonNull TabExpansion getTabExpansion();
|
||||
|
||||
public abstract @Nullable TabFeature getPetFix();
|
||||
|
||||
public abstract @Nullable TabFeature getGlobalPlayerlist();
|
||||
|
||||
public abstract @Nullable RedisSupport getRedisSupport();
|
||||
|
||||
public abstract @Nullable TabFeature getPerWorldPlayerlist();
|
||||
|
||||
@@ -66,7 +66,7 @@ public class TAB extends TabAPI {
|
||||
* occur in any part of the code including hooks into other plugins
|
||||
* into files instead of flooding the already flooded console.
|
||||
*/
|
||||
@Getter private ErrorManager errorManager;
|
||||
@Getter private final ErrorManager errorManager;
|
||||
|
||||
/** Feature manager forwarding events into all loaded features */
|
||||
@Getter private FeatureManagerImpl featureManager;
|
||||
@@ -113,6 +113,7 @@ public class TAB extends TabAPI {
|
||||
this.serverVersionString = serverVersionString;
|
||||
this.dataFolder = dataFolder;
|
||||
this.logger = logger;
|
||||
this.errorManager = new ErrorManager(this);
|
||||
try {
|
||||
eventBus = new EventBusImpl();
|
||||
} catch (NoSuchMethodError e) {
|
||||
@@ -141,7 +142,6 @@ public class TAB extends TabAPI {
|
||||
public String load() {
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
errorManager = new ErrorManager();
|
||||
cpu = new CpuManager();
|
||||
configuration = new Configs();
|
||||
featureManager = new FeatureManagerImpl();
|
||||
|
||||
@@ -2,7 +2,6 @@ package me.neznamy.tab.shared.backend;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
@@ -11,7 +10,6 @@ import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
|
||||
public abstract class BackendPlatform extends Platform {
|
||||
|
||||
@Getter private final TabFeature globalPlayerlist = null;
|
||||
@Getter private final RedisSupport redisSupport = null;
|
||||
|
||||
@Override
|
||||
|
||||
+88
-45
@@ -1,15 +1,17 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutSpawnEntityLiving;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class BackendArmorStand implements ArmorStand {
|
||||
public class BackendArmorStand implements ArmorStand {
|
||||
|
||||
/** Entity id counter to pick unique entity ID for each armor stand */
|
||||
private static int idCounter = 2000000000;
|
||||
@@ -35,9 +37,6 @@ public abstract class BackendArmorStand implements ArmorStand {
|
||||
/** Unique ID of this armor stand */
|
||||
protected final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/** Sneaking flag of armor stands */
|
||||
protected boolean sneaking;
|
||||
|
||||
/** Armor stand visibility to use in metadata */
|
||||
protected boolean visible;
|
||||
|
||||
@@ -96,31 +95,6 @@ public abstract class BackendArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sneak(boolean sneaking) {
|
||||
if (this.sneaking == sneaking) return; //idk
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !manager.isArmorStandsAlwaysVisible()) {
|
||||
//1.14.x client sided bug, de-spawning completely
|
||||
if (sneaking) {
|
||||
destroy(viewer);
|
||||
} else {
|
||||
spawn(viewer);
|
||||
}
|
||||
} else {
|
||||
//respawning so there's no animation and it's instant
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers())
|
||||
destroy(all);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
@@ -137,6 +111,13 @@ public abstract class BackendArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(TabPlayer viewer) {
|
||||
for (TabPacket packet : getSpawnPackets(viewer)) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general visibility rule for everyone with limited info
|
||||
*
|
||||
@@ -196,19 +177,12 @@ public abstract class BackendArmorStand implements ArmorStand {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void respawn(TabPlayer viewer) {
|
||||
destroy(viewer);
|
||||
// 1.8.0 will not see entity that respawned in the same tick
|
||||
// creating new delayed task every time someone sneaks can be abused and cause OOM
|
||||
spawn(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata for everyone
|
||||
*/
|
||||
public void updateMetadata() {
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
updateMetadata(viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,10 +192,79 @@ public abstract class BackendArmorStand implements ArmorStand {
|
||||
(owner.hasInvisibilityPotion() && viewer.getGamemode() != 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata for specified viewer
|
||||
*/
|
||||
public abstract void updateMetadata(TabPlayer viewer);
|
||||
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = calculateVisibility();
|
||||
Object dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, manager.getArmorStandType(),
|
||||
manager.getX(owner), getYLocation(viewer), manager.getZ(owner), 0, 0, null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, manager.getArmorStandType(),
|
||||
manager.getX(owner), getYLocation(viewer), manager.getZ(owner), 0, 0, dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void sendTeleportPacket(TabPlayer viewer);
|
||||
public void sendTeleportPacket(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityTeleport(entityId, manager.getX(owner), getYLocation(viewer),
|
||||
manager.getZ(owner), 0, 0), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
*
|
||||
* @param displayName
|
||||
* armor stand name
|
||||
* @param viewer
|
||||
* player to apply checks against
|
||||
* @return DataWatcher for viewer
|
||||
*/
|
||||
public Object createDataWatcher(String displayName, TabPlayer viewer) {
|
||||
byte flags = (byte) (asm.isSneaking() ? 34 : 32);
|
||||
boolean nameVisible = !shouldBeInvisibleFor(viewer, displayName) && visible;
|
||||
boolean marker = viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x();
|
||||
return manager.createDataWatcher(viewer, flags, displayName, nameVisible, marker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Y location where armor stand should be at time of calling.
|
||||
* This takes into account everything that affects height, including
|
||||
* viewer's game version.
|
||||
*
|
||||
* @param viewer
|
||||
* Player looking at the armor stand
|
||||
* @return Location where armor stand should be for specified viewer
|
||||
*/
|
||||
public double getYLocation(TabPlayer viewer) {
|
||||
double y = manager.getY(owner.getPlayer());
|
||||
//1.14+ server sided bug
|
||||
Object vehicle = manager.getVehicle(owner);
|
||||
if (vehicle != null) {
|
||||
String type = manager.getEntityType(vehicle);
|
||||
double vehicleY = manager.getY(vehicle);
|
||||
if (type.contains("horse")) { //covering all 3 horse types
|
||||
y = vehicleY + 0.85;
|
||||
}
|
||||
if (type.equals("donkey")) { //1.11+
|
||||
y = vehicleY + 0.525;
|
||||
}
|
||||
if (type.equals("pig")) {
|
||||
y = vehicleY + 0.325;
|
||||
}
|
||||
if (type.equals("strider")) { //1.16+
|
||||
y = vehicleY + 1.15;
|
||||
}
|
||||
} else {
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (manager.isSwimming(owner) || manager.isGliding(owner)) {
|
||||
y = manager.getY(owner.getPlayer())-1.22;
|
||||
}
|
||||
}
|
||||
y += getYAdd(manager.isSleeping(owner), asm.isSneaking(), viewer);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
+69
-20
@@ -1,31 +1,37 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.shared.backend.protocol.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class BackendArmorStandManager implements ArmorStandManager {
|
||||
|
||||
private final BackendNameTagX nameTagX;
|
||||
|
||||
@Getter private boolean sneaking;
|
||||
|
||||
/** Map of registered armor stands by name */
|
||||
private final Map<String, BackendArmorStand> armorStands = new LinkedHashMap<>();
|
||||
|
||||
/** Armor stands in an array for speed while iterating */
|
||||
private BackendArmorStand[] armorStandArray = new BackendArmorStand[0];
|
||||
private BackendArmorStand[] armorStandArray;
|
||||
|
||||
/** Players in entity tracking range of owner */
|
||||
private final List<TabPlayer> nearbyPlayerList = new ArrayList<>();
|
||||
|
||||
/** Nearby players in an array for speed while iterating */
|
||||
@Getter
|
||||
private TabPlayer[] nearbyPlayers = new TabPlayer[0];
|
||||
@Getter private TabPlayer[] nearbyPlayers = new TabPlayer[0];
|
||||
|
||||
/**
|
||||
* Packets to destroy armor stands. On 1.17 it's a list of individual armor stands
|
||||
* due to packet only containing one entity, on all other server versions
|
||||
* it's a single packet with all armor stand ids.
|
||||
*/
|
||||
private final PacketPlayOutEntityDestroy[] destroyPackets;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads armor stands.
|
||||
@@ -36,18 +42,29 @@ public class BackendArmorStandManager implements ArmorStandManager {
|
||||
* Owner of this armor stand manager
|
||||
*/
|
||||
public BackendArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
|
||||
this.nameTagX = (BackendNameTagX) nameTagX;
|
||||
sneaking = this.nameTagX.isSneaking(owner);
|
||||
owner.setProperty(nameTagX, TabConstants.Property.NAMETAG, owner.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
double height = 0;
|
||||
for (String line : nameTagX.getDynamicLines()) {
|
||||
addArmorStand(line, ((BackendNameTagX)nameTagX).createArmorStand(this, owner, line, height, false));
|
||||
armorStands.put(line, new BackendArmorStand((BackendNameTagX) nameTagX, this, owner, line, height, false));
|
||||
height += 0.26;
|
||||
}
|
||||
for (Map.Entry<String, Object> line : nameTagX.getStaticLines().entrySet()) {
|
||||
addArmorStand(line.getKey(), ((BackendNameTagX)nameTagX).createArmorStand(this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
armorStands.put(line.getKey(), new BackendArmorStand((BackendNameTagX) nameTagX, this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
}
|
||||
armorStandArray = armorStands.values().toArray(new BackendArmorStand[0]);
|
||||
fixArmorStandHeights();
|
||||
|
||||
List<PacketPlayOutEntityDestroy> destroyPackets = new ArrayList<>();
|
||||
if (TabAPI.getInstance().getServerVersion() == ProtocolVersion.V1_17) { // Mojank
|
||||
armorStands.values().forEach(as -> destroyPackets.add(new PacketPlayOutEntityDestroy(as.getEntityId())));
|
||||
} else {
|
||||
destroyPackets.add(new PacketPlayOutEntityDestroy(armorStands.values().stream().mapToInt(BackendArmorStand::getEntityId).toArray()));
|
||||
}
|
||||
this.destroyPackets = destroyPackets.toArray(new PacketPlayOutEntityDestroy[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,17 +118,41 @@ public class BackendArmorStandManager implements ArmorStandManager {
|
||||
* new sneaking status
|
||||
*/
|
||||
public void sneak(boolean sneaking) {
|
||||
for (ArmorStand a : armorStandArray) a.sneak(sneaking);
|
||||
if (this.sneaking == sneaking) return;
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : nearbyPlayers) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !nameTagX.isArmorStandsAlwaysVisible()) {
|
||||
//1.14.x client sided bug, de-spawning completely
|
||||
if (sneaking) {
|
||||
destroy(viewer);
|
||||
} else {
|
||||
spawn(viewer);
|
||||
}
|
||||
} else {
|
||||
//respawning so there's no animation and it's instant
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs respawn operation on all armor stands to skip teleport animation
|
||||
*/
|
||||
public void respawn() {
|
||||
for (TabPlayer viewer : nearbyPlayers) {
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
public void respawn(TabPlayer viewer) {
|
||||
// 1.8.0 will not see entity that respawned in the same tick
|
||||
// creating new delayed task every time someone sneaks can be abused and cause OOM
|
||||
// RIP 1.8.0
|
||||
for (PacketPlayOutEntityDestroy packet : destroyPackets) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
for (TabPlayer viewer : nearbyPlayers) {
|
||||
a.respawn(viewer);
|
||||
}
|
||||
a.spawn(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,13 +218,19 @@ public class BackendArmorStandManager implements ArmorStandManager {
|
||||
* player to destroy armor stands for
|
||||
*/
|
||||
public void destroy(TabPlayer viewer) {
|
||||
for (ArmorStand a : armorStandArray) a.destroy(viewer);
|
||||
for (PacketPlayOutEntityDestroy packet : destroyPackets) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
unregisterPlayer(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (ArmorStand a : armorStandArray) a.destroy();
|
||||
for (TabPlayer viewer : nearbyPlayers) {
|
||||
for (PacketPlayOutEntityDestroy packet : destroyPackets) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
}
|
||||
nearbyPlayerList.clear();
|
||||
nearbyPlayers = new TabPlayer[0];
|
||||
}
|
||||
@@ -201,6 +248,8 @@ public class BackendArmorStandManager implements ArmorStandManager {
|
||||
}
|
||||
|
||||
public void updateMetadata(TabPlayer viewer) {
|
||||
for (BackendArmorStand a : armorStandArray) a.updateMetadata(viewer);
|
||||
for (BackendArmorStand a : armorStandArray) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(a.getEntityId(), a.createDataWatcher(a.getProperty().getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-3
@@ -19,8 +19,11 @@ public abstract class BackendNameTagX extends NameTagX {
|
||||
super(BackendArmorStandManager::new);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, packetListener);
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%gamemode%", 500, TabPlayer::getGamemode);
|
||||
TabFeature gamemode = new TabFeature("Unlimited NameTags", "Gamemode listener") {
|
||||
TabFeature gamemode = new TabFeature() {
|
||||
|
||||
@Getter private final String featureName = "Unlimited NameTags";
|
||||
@Getter private final String refreshDisplayName = "Gamemode listener";
|
||||
|
||||
{
|
||||
addUsedPlaceholders(Collections.singletonList("%gamemode%"));
|
||||
}
|
||||
@@ -55,6 +58,7 @@ public abstract class BackendNameTagX extends NameTagX {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%gamemode%", 500, TabPlayer::getGamemode);
|
||||
super.load();
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
@@ -194,5 +198,21 @@ public abstract class BackendNameTagX extends NameTagX {
|
||||
|
||||
public abstract String getEntityType(Object entity);
|
||||
|
||||
public abstract BackendArmorStand createArmorStand(BackendArmorStandManager feature, TabPlayer owner, String lineName, double yOffset, boolean staticOffset);
|
||||
public abstract boolean isSneaking(TabPlayer player);
|
||||
|
||||
public abstract boolean isSwimming(TabPlayer player);
|
||||
|
||||
public abstract boolean isGliding(TabPlayer player);
|
||||
|
||||
public abstract boolean isSleeping(TabPlayer player);
|
||||
|
||||
public abstract Object getArmorStandType();
|
||||
|
||||
public abstract double getX(TabPlayer player);
|
||||
|
||||
public abstract double getY(Object entity);
|
||||
|
||||
public abstract double getZ(TabPlayer player);
|
||||
|
||||
public abstract Object createDataWatcher(TabPlayer viewer, byte flags, String displayName, boolean nameVisible, boolean markerFlag);
|
||||
}
|
||||
+5
-11
@@ -1,5 +1,7 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
@@ -17,25 +19,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* For entering/leaving tracking range there are no events and
|
||||
* periodic / move-triggered distance checks would cause high CPU usage.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class PacketListener extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Unlimited NameTags";
|
||||
|
||||
/** Reference to the main feature */
|
||||
protected final BackendNameTagX nameTagX;
|
||||
|
||||
/** A player map by entity id, used for better performance */
|
||||
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param nameTagX
|
||||
* main feature
|
||||
*/
|
||||
public PacketListener(BackendNameTagX nameTagX) {
|
||||
super(nameTagX.getFeatureName(), null);
|
||||
this.nameTagX = nameTagX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
|
||||
+7
-13
@@ -1,6 +1,7 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
@@ -18,8 +19,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* Additionally, when entering a vehicle, no move packet is sent
|
||||
* and therefore manual teleporting of armor stands is required.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class VehicleRefresher extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "NameTags";
|
||||
@Getter private final String refreshDisplayName = "Refreshing vehicles";
|
||||
|
||||
/** Map of players currently in a vehicle */
|
||||
private final WeakHashMap<TabPlayer, Object> playersInVehicle = new WeakHashMap<>();
|
||||
|
||||
@@ -33,15 +38,8 @@ public class VehicleRefresher extends TabFeature {
|
||||
/** Reference to the main feature */
|
||||
private final BackendNameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter and starts tasks.
|
||||
*
|
||||
* @param feature
|
||||
* Main feature
|
||||
*/
|
||||
public VehicleRefresher(BackendNameTagX feature) {
|
||||
super(feature.getFeatureName(), "Refreshing vehicles");
|
||||
this.feature = feature;
|
||||
@Override
|
||||
public void load() {
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(50,
|
||||
this, TabConstants.CpuUsageCategory.PROCESSING_PLAYER_MOVEMENT, () -> {
|
||||
for (TabPlayer inVehicle : playersInVehicle.keySet()) {
|
||||
@@ -56,10 +54,6 @@ public class VehicleRefresher extends TabFeature {
|
||||
});
|
||||
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VEHICLE));
|
||||
feature.registerVehiclePlaceholder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
Object vehicle = feature.getVehicle(p);
|
||||
if (vehicle != null) {
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package me.neznamy.tab.shared.backend.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
|
||||
/** Packet's instance fields */
|
||||
private final int[] entities;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param entities
|
||||
* Destroyed entities
|
||||
*/
|
||||
public PacketPlayOutEntityDestroy(int... entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,9 +1,8 @@
|
||||
package me.neznamy.tab.platforms.sponge8.nms;
|
||||
package me.neznamy.tab.shared.backend.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityMetadata minecraft packet.
|
||||
@@ -14,6 +13,6 @@ public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's data that changed */
|
||||
@NonNull private final SynchedEntityData dataWatcher;
|
||||
/** Entity's platform specific data watcher object */
|
||||
@NonNull private final Object dataWatcher;
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8.nms;
|
||||
package me.neznamy.tab.shared.backend.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
@@ -11,6 +11,8 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity location */
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8.nms;
|
||||
package me.neznamy.tab.shared.backend.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
@@ -19,11 +19,14 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
@NonNull private final UUID uniqueId;
|
||||
|
||||
/** Type of entity */
|
||||
private final int entityType;
|
||||
private final Object entityType;
|
||||
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
private final float yaw;
|
||||
private final float pitch;
|
||||
|
||||
/** Data Watcher for 1.14- */
|
||||
private final Object dataWatcher;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class TabCommand extends SubCommand {
|
||||
registerSubCommand(new SetCollisionCommand());
|
||||
registerSubCommand(new ScoreboardCommand());
|
||||
registerSubCommand(new WidthCommand());
|
||||
//registerSubCommand(new WidthCheckCommand());
|
||||
registerSubCommand(new WidthCheckCommand());
|
||||
List<String> properties = Lists.newArrayList(TabConstants.Property.TABPREFIX, TabConstants.Property.TABSUFFIX, TabConstants.Property.TAGPREFIX, TabConstants.Property.TAGSUFFIX, TabConstants.Property.CUSTOMTABNAME, TabConstants.Property.ABOVENAME, TabConstants.Property.BELOWNAME, TabConstants.Property.CUSTOMTAGNAME);
|
||||
properties.addAll(((DebugCommand) getSubcommands().get("debug")).getExtraLines());
|
||||
SubCommand.setAllProperties(properties.toArray(new String[0]));
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class WidthCheckCommand extends SubCommand {
|
||||
|
||||
private final byte[] widths = loadWidths();
|
||||
private byte[] widths;
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
@@ -46,14 +46,15 @@ public class WidthCheckCommand extends SubCommand {
|
||||
|
||||
@Override
|
||||
public void execute(TabPlayer sender, String[] args) {
|
||||
if (widths == null) widths = loadWidths();
|
||||
if (sender == null) {
|
||||
sendMessage(null, getMessages().getCommandOnlyFromGame());
|
||||
return;
|
||||
}
|
||||
if (args.length == 1) {
|
||||
int i = Integer.parseInt(args[0]);
|
||||
if (i > Character.MAX_VALUE) {
|
||||
sendMessage(sender, "&cCharacter ID out of range: 0-" + (int) Character.MAX_VALUE);
|
||||
if (i < 0 || i > Character.MAX_VALUE) {
|
||||
sendMessage(sender, "&cCharacter ID out of range: 0-" + (int) Character.MAX_VALUE + " (was " + i + ")");
|
||||
return;
|
||||
}
|
||||
int ROWS = 15;
|
||||
|
||||
@@ -27,8 +27,9 @@ public class Configs {
|
||||
|
||||
@Getter private final boolean bukkitPermissions = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY && config.getBoolean("use-bukkit-permissions-manager", false);
|
||||
@Getter private final boolean debugMode = config.getBoolean("debug", false);
|
||||
@Getter private final boolean onlineUuidInTabList = config.getBoolean("use-online-uuid-in-tablist", true);
|
||||
@Getter private final boolean removeGhostPlayers = getSecretOption("remove-ghost-players", false);
|
||||
@Getter private final boolean pipelineInjection = getSecretOption("pipeline-injection", true) && TAB.getInstance().getServerVersion().getMinorVersion() >= 8;
|
||||
@Getter private final boolean pipelineInjection = getSecretOption("pipeline-injection", true);
|
||||
@Getter private final String serverName = getSecretOption("server-name", "N/A");
|
||||
|
||||
//animations.yml file
|
||||
|
||||
@@ -268,7 +268,7 @@ public class Converter {
|
||||
}
|
||||
newConfig.set("scoreboard.scoreboards.admin.display-condition", "permission:tab.scoreboard.admin");
|
||||
newConfig.set("scoreboard.scoreboards.admin.title", "Admin scoreboard");
|
||||
newConfig.set("scoreboard.scoreboards.admin.lines", Arrays.asList("%animation:MyAnimation1%", "&6Online:", "* &eOnline&7: &f%online%&7/&4%maxplayers%",
|
||||
newConfig.set("scoreboard.scoreboards.admin.lines", Arrays.asList("%animation:MyAnimation1%", "&6Online:", "* &eOnline&7: &f%online%&7",
|
||||
"* &eCurrent World&7: &f%worldonline%", "* &eStaff&7: &f%staffonline%", " ", "&6Server Info:", "* &bTPS&7: %tps%",
|
||||
"* &bUptime&7: &f%server_uptime%", "* &bMemory&7: &f%memory-used%&7/&4%memory-max%", "%animation:MyAnimation1%"));
|
||||
newConfig.set("scoreboard.scoreboards.scoreboard1.title", "Default");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
@@ -11,9 +11,12 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboa
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore.Action;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Feature handler for BelowName feature
|
||||
*/
|
||||
@@ -22,14 +25,17 @@ public class BelowName extends TabFeature {
|
||||
public static final String OBJECTIVE_NAME = "TAB-BelowName";
|
||||
public static final int DISPLAY_SLOT = 2;
|
||||
|
||||
@Getter private final String refreshDisplayName = "Updating BelowName number";
|
||||
@Getter private final String featureName = "BelowName";
|
||||
private final String rawNumber = TAB.getInstance().getConfiguration().getConfig().getString("belowname-objective.number", TabConstants.Placeholder.HEALTH);
|
||||
private final String rawText = TAB.getInstance().getConfiguration().getConfig().getString("belowname-objective.text", "Health");
|
||||
private final TabFeature textRefresher = new TextRefresher();
|
||||
private final TabFeature textRefresher = new TextRefresher(this);
|
||||
|
||||
private final RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
|
||||
public BelowName() {
|
||||
super("BelowName", "Updating BelowName number", "belowname-objective");
|
||||
super("belowname-objective");
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.BELOW_NAME_TEXT, textRefresher);
|
||||
TAB.getInstance().debug(String.format("Loaded BelowName feature with parameters number=%s, text=%s, disabledWorlds=%s, disabledServers=%s", rawNumber, rawText, Arrays.toString(disabledWorlds), Arrays.toString(disabledServers)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,11 +50,16 @@ public class BelowName extends TabFeature {
|
||||
loaded.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, OBJECTIVE_NAME, loaded.getProperty(TabConstants.Property.BELOWNAME_TEXT).updateAndGet(), EnumScoreboardHealthDisplay.INTEGER), textRefresher);
|
||||
loaded.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(DISPLAY_SLOT, OBJECTIVE_NAME), textRefresher);
|
||||
}
|
||||
Map<TabPlayer, Integer> values = new HashMap<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (isDisabledPlayer(target)) continue;
|
||||
values.put(target, getValue(target));
|
||||
}
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (isDisabledPlayer(viewer)) continue;
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (sameServerAndWorld(target, viewer)) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, target.getNickname(), getValue(target)), this);
|
||||
for (Map.Entry<TabPlayer, Integer> entry : values.entrySet()) {
|
||||
if (sameServerAndWorld(entry.getKey(), viewer)) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, entry.getKey().getNickname(), entry.getValue()), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +90,6 @@ public class BelowName extends TabFeature {
|
||||
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, all.getNickname(), getValue(all)), this);
|
||||
}
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateBelowName(connectedPlayer, connectedPlayer.getProperty(TabConstants.Property.BELOWNAME_NUMBER).get());
|
||||
}
|
||||
|
||||
@@ -114,7 +124,6 @@ public class BelowName extends TabFeature {
|
||||
p.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, all.getNickname(), getValue(all)), this);
|
||||
}
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateBelowName(p, p.getProperty(TabConstants.Property.BELOWNAME_NUMBER).get());
|
||||
}
|
||||
|
||||
@@ -130,7 +139,6 @@ public class BelowName extends TabFeature {
|
||||
if (sameServerAndWorld(all, refreshed))
|
||||
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, refreshed.getNickname(), number), this);
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateBelowName(refreshed, refreshed.getProperty(TabConstants.Property.BELOWNAME_NUMBER).get());
|
||||
}
|
||||
|
||||
@@ -150,15 +158,16 @@ public class BelowName extends TabFeature {
|
||||
return player2.getWorld().equals(player1.getWorld()) && Objects.equals(player2.getServer(), player1.getServer());
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class TextRefresher extends TabFeature {
|
||||
|
||||
public TextRefresher() {
|
||||
super("BelowName", "Updating BelowName text");
|
||||
}
|
||||
@Getter private final String refreshDisplayName = "Updating BelowName text";
|
||||
@Getter private final String featureName = "BelowName";
|
||||
private final BelowName feature;
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (isDisabledPlayer(refreshed)) return;
|
||||
if (feature.isDisabledPlayer(refreshed)) return;
|
||||
refreshed.sendCustomPacket(new PacketPlayOutScoreboardObjective(2, OBJECTIVE_NAME, refreshed.getProperty(TabConstants.Property.BELOWNAME_TEXT).updateAndGet(), EnumScoreboardHealthDisplay.INTEGER), textRefresher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
@@ -13,11 +14,8 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public class GhostPlayerFix extends TabFeature {
|
||||
|
||||
public GhostPlayerFix() {
|
||||
super("Ghost player fix", null);
|
||||
TAB.getInstance().debug("Loaded GhostPlayerFix feature");
|
||||
}
|
||||
|
||||
@Getter private final String featureName = "Ghost player fix";
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
TAB.getInstance().getCPUManager().runTaskLater(500, this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.HeaderFooterManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
@@ -8,7 +9,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerListHeaderFooter;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -16,12 +16,13 @@ import java.util.List;
|
||||
*/
|
||||
public class HeaderFooter extends TabFeature implements HeaderFooterManager {
|
||||
|
||||
@Getter private final String featureName = "Header/Footer";
|
||||
@Getter private final String refreshDisplayName = "Updating header/footer";
|
||||
private final List<Object> worldGroups = new ArrayList<>(TAB.getInstance().getConfig().getConfigurationSection("header-footer.per-world").keySet());
|
||||
private final List<Object> serverGroups = new ArrayList<>(TAB.getInstance().getConfig().getConfigurationSection("header-footer.per-server").keySet());
|
||||
|
||||
public HeaderFooter() {
|
||||
super("Header/Footer", "Updating header/footer", "header-footer");
|
||||
TAB.getInstance().debug(String.format("Loaded HeaderFooter feature with parameters disabledWorlds=%s, disabledServers=%s", Arrays.toString(disabledWorlds), Arrays.toString(disabledServers)));
|
||||
super("header-footer");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.features;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
@@ -20,21 +21,11 @@ import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
|
||||
public class NickCompatibility extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Nick compatibility";
|
||||
private final NameTag nameTags = (NameTag) TAB.getInstance().getTeamManager();
|
||||
private final BelowName belowname = (BelowName) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BELOW_NAME);
|
||||
private final YellowNumber yellownumber = (YellowNumber) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.YELLOW_NUMBER);
|
||||
private RedisSupport redis;
|
||||
|
||||
public NickCompatibility() {
|
||||
super("Nick compatibility", null);
|
||||
TAB.getInstance().debug("Loaded NickCompatibility feature");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// redis feature is instantiated after nick compatibility
|
||||
redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
}
|
||||
private final RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
|
||||
@Override
|
||||
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo packet) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -16,6 +13,10 @@ import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.layout.ParentGroup;
|
||||
import me.neznamy.tab.shared.features.layout.PlayerSlot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sets ping of all players in the packet to configured value to prevent hacked clients from seeing exact ping value of each player
|
||||
*/
|
||||
@@ -24,15 +25,9 @@ public class PingSpoof extends TabFeature {
|
||||
//fake ping value
|
||||
private final int value = TAB.getInstance().getConfiguration().getConfig().getInt("ping-spoof.value", 0);
|
||||
|
||||
private LayoutManager layoutManager;
|
||||
private final LayoutManager layoutManager = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads config options
|
||||
*/
|
||||
public PingSpoof() {
|
||||
super("Ping spoof", null);
|
||||
TAB.getInstance().debug(String.format("Loaded PingSpoof feature with parameters value=%s", value));
|
||||
}
|
||||
@Getter private final String featureName = "Ping spoof";
|
||||
|
||||
@Override
|
||||
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
@@ -56,7 +51,6 @@ public class PingSpoof extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
layoutManager = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
|
||||
updateAll(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* Packet intercepting to secure proper functionality of some features:
|
||||
* TabList names - anti-override
|
||||
* NameTags - anti-override
|
||||
* Scoreboard - disabling tab's scoreboard to prevent conflict
|
||||
* SpectatorFix - to change game mode to something else than spectator
|
||||
* PetFix - to remove owner field from entity data
|
||||
* PingSpoof - full feature functionality
|
||||
* Unlimited name tags - replacement for bukkit events with much better accuracy and reliability
|
||||
*/
|
||||
public abstract class PipelineInjector extends TabFeature {
|
||||
|
||||
//name of the pipeline decoder injected in netty
|
||||
public final String DECODER_NAME = TabConstants.PIPELINE_HANDLER_NAME;
|
||||
|
||||
//handler to inject before
|
||||
private final String injectPosition;
|
||||
|
||||
//preventing spam when packet is sent to everyone
|
||||
private String lastTeamOverrideMessage;
|
||||
|
||||
//anti-override rules
|
||||
@Setter protected boolean antiOverrideTeams;
|
||||
|
||||
@Setter protected boolean byteBufDeserialization;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param injectPosition
|
||||
* position to inject handler before
|
||||
*/
|
||||
protected PipelineInjector(String injectPosition) {
|
||||
super("Pipeline injection", null);
|
||||
this.injectPosition = injectPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects custom channel duplex handler to prevent other plugins from overriding this one
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
public void inject(TabPlayer player) {
|
||||
if (player.getVersion().getMinorVersion() < 8 || player.getChannel() == null) return; //hello A248
|
||||
if (!player.getChannel().pipeline().names().contains(injectPosition)) {
|
||||
//fake player or waterfall bug
|
||||
return;
|
||||
}
|
||||
uninject(player);
|
||||
try {
|
||||
player.getChannel().pipeline().addBefore(injectPosition, DECODER_NAME, getChannelFunction().apply(player));
|
||||
} catch (NoSuchElementException | IllegalArgumentException e) {
|
||||
//I don't really know how does this keep happening but whatever
|
||||
}
|
||||
}
|
||||
|
||||
public void uninject(TabPlayer player) {
|
||||
if (player.getVersion().getMinorVersion() < 8 || player.getChannel() == null) return; //hello A248
|
||||
try {
|
||||
if (player.getChannel().pipeline().names().contains(DECODER_NAME)) player.getChannel().pipeline().remove(DECODER_NAME);
|
||||
} catch (NoSuchElementException e) {
|
||||
//for whatever reason this rarely throws
|
||||
//java.util.NoSuchElementException: TABReader
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
inject(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
uninject(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
inject(connectedPlayer);
|
||||
}
|
||||
|
||||
protected void logTeamOverride(String team, String player, String expectedTeam) {
|
||||
String message = "Something just tried to add player " + player + " into team " + team + " (expected team: " + expectedTeam + ")";
|
||||
//not logging the same message for every online player who received the packet
|
||||
if (!message.equals(lastTeamOverrideMessage)) {
|
||||
lastTeamOverrideMessage = message;
|
||||
TAB.getInstance().getErrorManager().printError(message, null, false, TAB.getInstance().getErrorManager().getAntiOverrideLog());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Function<TabPlayer, ChannelDuplexHandler> getChannelFunction();
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.Placeholder;
|
||||
@@ -28,6 +28,8 @@ import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
|
||||
import me.neznamy.tab.shared.placeholders.RelationalPlaceholderImpl;
|
||||
import me.neznamy.tab.shared.placeholders.ServerPlaceholderImpl;
|
||||
import me.neznamy.tab.shared.placeholders.TabPlaceholder;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
|
||||
/**
|
||||
* Messy class for placeholder management
|
||||
@@ -36,6 +38,8 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
|
||||
|
||||
private final Pattern placeholderPattern = Pattern.compile("%([^%]*)%");
|
||||
|
||||
@Getter private final String refreshDisplayName = "Updating placeholders";
|
||||
@Getter private final String featureName = "Refreshing placeholders";
|
||||
@Getter private final int defaultRefresh = TAB.getInstance().getConfiguration().getConfig().getInt("placeholderapi-refresh-intervals.default-refresh-interval", 100);
|
||||
@Getter private final Map<String, Integer> serverPlaceholderRefreshIntervals = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("placeholderapi-refresh-intervals.server");
|
||||
@Getter private final Map<String, Integer> playerPlaceholderRefreshIntervals = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("placeholderapi-refresh-intervals.player");
|
||||
@@ -52,12 +56,9 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
|
||||
private int refreshInterval = 10000;
|
||||
private Future<?> refreshTask = TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(refreshInterval, this, "Refreshing placeholders", this::refresh);
|
||||
|
||||
@Getter @Setter private TabExpansion tabExpansion;
|
||||
@Getter @NonNull private final TabExpansion tabExpansion = TAB.getInstance().getConfig().getBoolean("placeholders.register-tab-expansion", false) ?
|
||||
TAB.getInstance().getPlatform().getTabExpansion() : new EmptyTabExpansion();
|
||||
|
||||
public PlaceholderManagerImpl() {
|
||||
super("Refreshing placeholders", "Updating placeholders");
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
int loopTime = this.loopTime.addAndGet(refreshInterval);
|
||||
int size = TAB.getInstance().getOnlinePlayers().length;
|
||||
@@ -232,10 +233,8 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
|
||||
recalculateUsedPlaceholders();
|
||||
TabPlaceholder p = getPlaceholder(identifier);
|
||||
p.markAsUsed();
|
||||
if (tabExpansion != null) {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
tabExpansion.setPlaceholderValue(all, p.getIdentifier(), p.getLastValue(all));
|
||||
}
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
tabExpansion.setPlaceholderValue(all, p.getIdentifier(), p.getLastValue(all));
|
||||
}
|
||||
if (p.getRefresh() % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL == 0 && p.getRefresh() > 0) {
|
||||
int refresh = gcd(p.getRefresh(), refreshInterval);
|
||||
@@ -280,7 +279,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
|
||||
if (p instanceof PlayerPlaceholderImpl) {
|
||||
((PlayerPlaceholderImpl)p).update(connectedPlayer);
|
||||
}
|
||||
if (tabExpansion != null && p instanceof ServerPlaceholder) { // server placeholders don't update on join
|
||||
if (p instanceof ServerPlaceholder) { // server placeholders don't update on join
|
||||
tabExpansion.setPlaceholderValue(connectedPlayer, p.getIdentifier(), ((ServerPlaceholder) p).getLastValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
@@ -17,14 +13,24 @@ import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.layout.PlayerSlot;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Feature handler for TabList display names
|
||||
*/
|
||||
public class PlayerList extends TabFeature implements TablistFormatManager {
|
||||
|
||||
@Getter private final String featureName = "Tablist name formatting";
|
||||
@Getter private final String refreshDisplayName = "Updating TabList format";
|
||||
|
||||
/** Config option toggling anti-override which prevents other plugins from overriding TAB */
|
||||
protected final boolean antiOverrideTabList = TAB.getInstance().getConfiguration().getConfig().getBoolean("tablist-name-formatting.anti-override", true);
|
||||
|
||||
private final LayoutManager layoutManager = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
|
||||
private RedisSupport redis;
|
||||
|
||||
/**
|
||||
* Flag tracking when the plugin is disabling to properly clear
|
||||
* display name by setting it to null value and not force the value back
|
||||
@@ -36,8 +42,7 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
|
||||
* Constructs new instance and sends debug message that feature loaded.
|
||||
*/
|
||||
public PlayerList() {
|
||||
super("TabList prefix/suffix", "Updating TabList format", "tablist-name-formatting");
|
||||
TAB.getInstance().debug(String.format("Loaded PlayerList feature with parameters disabledWorlds=%s, disabledServers=%s, antiOverrideTabList=%s", Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), antiOverrideTabList));
|
||||
super("tablist-name-formatting");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,9 +58,8 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
|
||||
* @return UUID of TabList entry representing requested player
|
||||
*/
|
||||
public UUID getTablistUUID(TabPlayer p, TabPlayer viewer) {
|
||||
LayoutManager manager = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
|
||||
if (manager != null) {
|
||||
Layout layout = manager.getPlayerViews().get(viewer);
|
||||
if (layoutManager != null) {
|
||||
Layout layout = layoutManager.getPlayerViews().get(viewer);
|
||||
if (layout != null) {
|
||||
PlayerSlot slot = layout.getSlot(p);
|
||||
if (slot != null) {
|
||||
@@ -95,7 +99,6 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
|
||||
viewer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
|
||||
new PlayerInfoData(getTablistUUID(p, viewer), format ? getTabFormat(p, viewer) : null)), this);
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateTabFormat(p, p.getProperty(TabConstants.Property.TABPREFIX).get() + p.getProperty(TabConstants.Property.CUSTOMTABNAME).get() + p.getProperty(TabConstants.Property.TABSUFFIX).get());
|
||||
}
|
||||
|
||||
@@ -120,13 +123,23 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
updateProperties(all);
|
||||
if (isDisabled(all.getServer(), all.getWorld())) {
|
||||
addDisabledPlayer(all);
|
||||
updateProperties(all);
|
||||
continue;
|
||||
} else {
|
||||
if (redis != null) redis.updateTabFormat(all, all.getProperty(TabConstants.Property.TABPREFIX).get() + all.getProperty(TabConstants.Property.CUSTOMTABNAME).get() + all.getProperty(TabConstants.Property.TABSUFFIX).get());
|
||||
}
|
||||
refresh(all, true);
|
||||
}
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) continue;
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (isDisabledPlayer(target)) continue;
|
||||
list.add(new PlayerInfoData(getTablistUUID(target, viewer), getTabFormat(target, viewer)));
|
||||
}
|
||||
viewer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, list), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.features;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
@@ -19,31 +20,25 @@ import me.neznamy.tab.api.TabConstants;
|
||||
*/
|
||||
public class SpectatorFix extends TabFeature {
|
||||
|
||||
/**
|
||||
* Constructs new instance and sends debug message that feature loaded.
|
||||
*/
|
||||
public SpectatorFix() {
|
||||
super("Spectator fix", null);
|
||||
TAB.getInstance().debug("Loaded SpectatorFix feature");
|
||||
}
|
||||
@Getter private final String featureName = "Spectator fix";
|
||||
|
||||
/**
|
||||
* Sends GameMode update of all players to either their real GameMode if
|
||||
* {@code realGameMode} is {@code true} or fake value if it's {@code false}.
|
||||
*
|
||||
* @param viewer
|
||||
* Player to send gamemode updates to
|
||||
* @param realGameMode
|
||||
* Whether real GameMode should be shown or fake one
|
||||
*/
|
||||
private void updateAll(boolean realGameMode) {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.hasPermission(TabConstants.Permission.SPECTATOR_BYPASS)) continue;
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p == target) continue;
|
||||
list.add(new PlayerInfoData(target.getUniqueId(), realGameMode ? EnumGamemode.VALUES[target.getGamemode()+1] : EnumGamemode.CREATIVE));
|
||||
}
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_GAME_MODE, list), this);
|
||||
private void updatePlayer(TabPlayer viewer, boolean realGameMode) {
|
||||
if (viewer.hasPermission(TabConstants.Permission.SPECTATOR_BYPASS)) return;
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer == target || target.getGamemode() != 3) continue;
|
||||
list.add(new PlayerInfoData(target.getUniqueId(), realGameMode ? EnumGamemode.VALUES[target.getGamemode()+1] : EnumGamemode.CREATIVE));
|
||||
}
|
||||
if (!list.isEmpty()) viewer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_GAME_MODE, list), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,22 +54,20 @@ public class SpectatorFix extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer p) {
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p == target || target.getGamemode() != 3) continue;
|
||||
list.add(new PlayerInfoData(target.getUniqueId(), EnumGamemode.CREATIVE));
|
||||
}
|
||||
if (list.isEmpty() || p.hasPermission(TabConstants.Permission.SPECTATOR_BYPASS)) return;
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_GAME_MODE, list), this);
|
||||
updatePlayer(p, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
updateAll(false);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
updatePlayer(viewer, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
updateAll(true);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
updatePlayer(viewer, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
@@ -10,7 +10,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboa
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore.Action;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
|
||||
/**
|
||||
@@ -19,6 +18,9 @@ import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
*/
|
||||
public class YellowNumber extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Yellow Number";
|
||||
@Getter private final String refreshDisplayName = "Updating value";
|
||||
|
||||
/** Objective name used by this feature */
|
||||
public static final String OBJECTIVE_NAME = "TAB-YellowNumber";
|
||||
|
||||
@@ -35,12 +37,13 @@ public class YellowNumber extends TabFeature {
|
||||
private final EnumScoreboardHealthDisplay displayType = TabConstants.Placeholder.HEALTH.equals(rawValue) || "%player_health%".equals(rawValue) ||
|
||||
"%player_health_rounded%".equals(rawValue) ? EnumScoreboardHealthDisplay.HEARTS : EnumScoreboardHealthDisplay.INTEGER;
|
||||
|
||||
private final RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
|
||||
/**
|
||||
* Constructs new instance and sends debug message that feature loaded.
|
||||
*/
|
||||
public YellowNumber() {
|
||||
super("Yellow number", "Updating value", "yellow-number-in-tablist");
|
||||
TAB.getInstance().debug(String.format("Loaded YellowNumber feature with parameters value=%s, disabledWorlds=%s, disabledServers=%s, displayType=%s", rawValue, Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), displayType));
|
||||
super("yellow-number-in-tablist");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +125,6 @@ public class YellowNumber extends TabFeature {
|
||||
}
|
||||
if (!disabledNow && disabledBefore) {
|
||||
onJoin(p);
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateYellowNumber(p, p.getProperty(TabConstants.Property.YELLOW_NUMBER).get());
|
||||
}
|
||||
}
|
||||
@@ -134,7 +136,6 @@ public class YellowNumber extends TabFeature {
|
||||
if (isDisabledPlayer(all) || all.isBedrockPlayer()) continue;
|
||||
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, refreshed.getNickname(), value), this);
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateYellowNumber(refreshed, refreshed.getProperty(TabConstants.Property.YELLOW_NUMBER).get());
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -22,10 +22,6 @@ public class AlignedPlayerList extends PlayerList {
|
||||
private final Map<TabPlayer, PlayerView> playerViews = new HashMap<>();
|
||||
@Getter private final byte[] widths = loadWidths();
|
||||
|
||||
public AlignedPlayerList() {
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads widths from included widths.txt file as well as width overrides from config
|
||||
*/
|
||||
@@ -56,6 +52,7 @@ public class AlignedPlayerList extends PlayerList {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
updateProperties(all);
|
||||
playerViews.put(all, new PlayerView(this, all));
|
||||
|
||||
@@ -93,7 +93,11 @@ public class BossBarLine implements BossBar {
|
||||
propertyColor = TabConstants.Property.bossbarColor(name);
|
||||
propertyStyle = TabConstants.Property.bossbarStyle(name);
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.bossBarTitle(name),
|
||||
textRefresher = new TabFeature(manager.getFeatureName(), "Updating text") {
|
||||
textRefresher = new TabFeature() {
|
||||
|
||||
@Getter private final String featureName = "BossBar";
|
||||
@Getter private final String refreshDisplayName = "Updating text";
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (!players.contains(refreshed)) return;
|
||||
@@ -101,7 +105,11 @@ public class BossBarLine implements BossBar {
|
||||
}
|
||||
});
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.bossBarProgress(name),
|
||||
progressRefresher = new TabFeature(manager.getFeatureName(), "Updating progress") {
|
||||
progressRefresher = new TabFeature() {
|
||||
|
||||
@Getter private final String featureName = "BossBar";
|
||||
@Getter private final String refreshDisplayName = "Updating progress";
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (!players.contains(refreshed)) return;
|
||||
@@ -109,7 +117,11 @@ public class BossBarLine implements BossBar {
|
||||
}
|
||||
});
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.bossBarColorStyle(name),
|
||||
colorAndStyleRefresher = new TabFeature(manager.getFeatureName(), "Updating color and style") {
|
||||
colorAndStyleRefresher = new TabFeature() {
|
||||
|
||||
@Getter private final String featureName = "BossBar";
|
||||
@Getter private final String refreshDisplayName = "Updating color and style";
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (!players.contains(refreshed)) return;
|
||||
|
||||
+13
-13
@@ -1,9 +1,7 @@
|
||||
package me.neznamy.tab.shared.features.bossbar;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
@@ -11,8 +9,9 @@ import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.api.bossbar.BossBarManager;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Class for handling BossBar feature
|
||||
@@ -27,7 +26,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
|
||||
private BossBar[] lineValues;
|
||||
|
||||
//config options
|
||||
private final String toggleCommand = TAB.getInstance().getConfiguration().getConfig().getString("bossbar.toggle-command", "/bossbar");
|
||||
@Getter private final String toggleCommand = TAB.getInstance().getConfiguration().getConfig().getString("bossbar.toggle-command", "/bossbar");
|
||||
private final boolean hiddenByDefault = TAB.getInstance().getConfiguration().getConfig().getBoolean("bossbar.hidden-by-default", false);
|
||||
private final boolean rememberToggleChoice = TAB.getInstance().getConfiguration().getConfig().getBoolean("bossbar.remember-toggle-choice", false);
|
||||
private final String toggleOnMessage = TAB.getInstance().getConfiguration().getMessages().getBossBarOn();
|
||||
@@ -37,27 +36,28 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
|
||||
@Getter private final List<BossBar> announcedBossBars = new ArrayList<>();
|
||||
|
||||
//players with toggled BossBar
|
||||
private final List<String> bossBarOffPlayers = rememberToggleChoice ? TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("bossbar-off", new ArrayList<>()) : Collections.emptyList();
|
||||
private final List<String> bossBarOffPlayers = rememberToggleChoice ? TAB.getInstance().getConfiguration().getPlayerDataFile()
|
||||
.getStringList("bossbar-off", new ArrayList<>()) : Collections.emptyList();
|
||||
|
||||
//time when BossBar announce ends, used for placeholder
|
||||
private long announceEndTime;
|
||||
|
||||
private final Set<TabPlayer> visiblePlayers = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
@Getter private final String featureName = "BossBar";
|
||||
@Getter private final String refreshDisplayName = "Updating display conditions";
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads configuration
|
||||
*/
|
||||
public BossBarManagerImpl() {
|
||||
super("BossBar", "Processing display conditions", "bossbar");
|
||||
super("bossbar");
|
||||
for (Object bar : TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("bossbar.bars").keySet()) {
|
||||
BossBarLine line = loadFromConfig(bar.toString());
|
||||
registeredBossBars.put(bar.toString(), line);
|
||||
if (!line.isAnnouncementBar()) defaultBars.add(bar.toString());
|
||||
}
|
||||
lineValues = registeredBossBars.values().toArray(new BossBar[0]);
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> (announceEndTime - System.currentTimeMillis()) / 1000);
|
||||
TAB.getInstance().debug(String.format("Loaded BossBar feature with parameters disabledWorlds=%s, disabledServers=%s, toggleCommand=%s, defaultBars=%s, hiddenByDefault=%s, remember_toggle_choice=%s",
|
||||
Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), toggleCommand, defaultBars, hiddenByDefault, rememberToggleChoice));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +95,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> (announceEndTime - System.currentTimeMillis()) / 1000);
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
onJoin(p);
|
||||
}
|
||||
@@ -250,8 +251,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
|
||||
TAB.getInstance().getConfiguration().getPlayerDataFile().set("bossbar-off", new ArrayList<>(bossBarOffPlayers));
|
||||
}
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setBossBarVisible(player, visible);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setBossBarVisible(player, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+60
-91
@@ -3,10 +3,10 @@ package me.neznamy.tab.shared.features.globalplayerlist;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.placeholder.ServerPlaceholder;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
@@ -28,33 +28,27 @@ public class GlobalPlayerList extends TabFeature {
|
||||
private final boolean vanishedAsSpectators = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.display-vanished-players-as-spectators", true);
|
||||
private final boolean isolateUnlistedServers = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.isolate-unlisted-servers", false);
|
||||
private final boolean fillProfileKey = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.fill-profile-key", false);
|
||||
private final boolean updateLatency = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.update-latency", false);
|
||||
|
||||
private final List<ServerPlaceholder> placeholders = new ArrayList<>();
|
||||
private final Map<TabPlayer, Long> lastServerSwitch = new WeakHashMap<>();
|
||||
private final UUID EMPTY_ID = new UUID(0, 0);
|
||||
|
||||
public GlobalPlayerList() {
|
||||
super("Global PlayerList", null);
|
||||
boolean updateLatency = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.update-latency", false);
|
||||
if (updateLatency) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST_LATENCY, new LatencyRefresher());
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
|
||||
for (Entry<String, List<String>> entry : sharedServers.entrySet()) {
|
||||
placeholders.add(TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.globalPlayerListGroup(entry.getKey()), -1,
|
||||
() -> Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(p -> entry.getValue().contains(p.getServer()) && !p.isVanished()).count()));
|
||||
}
|
||||
TAB.getInstance().debug(String.format("Loaded GlobalPlayerList feature with parameters spyServers=%s, sharedServers=%s, displayAsSpectators=%s, vanishedAsSpectators=%s, isolateUnlistedServers=%s, updateLatency=%s",
|
||||
spyServers, sharedServers, displayAsSpectators, vanishedAsSpectators, isolateUnlistedServers, updateLatency));
|
||||
}
|
||||
private final PlayerList playerlist = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
@Getter private final String featureName = "Global PlayerList";
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer displayed : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getServer().equals(displayed.getServer())) continue;
|
||||
if (shouldSee(viewer, displayed)) viewer.sendCustomPacket(getAddPacket(displayed, viewer), this);
|
||||
}
|
||||
if (updateLatency) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST_LATENCY, new LatencyRefresher());
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
|
||||
for (Entry<String, List<String>> entry : sharedServers.entrySet()) {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.globalPlayerListGroup(entry.getKey()), 1000,
|
||||
() -> Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(p -> entry.getValue().contains(p.getServer()) && !p.isVanished()).count());
|
||||
}
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
List<PlayerInfoData> entries = new ArrayList<>();
|
||||
for (TabPlayer displayed : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getServer().equals(displayed.getServer())) continue;
|
||||
if (shouldSee(viewer, displayed)) entries.add(getAddInfoData(displayed, viewer));
|
||||
}
|
||||
if (!entries.isEmpty()) viewer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, entries), this);
|
||||
}
|
||||
placeholders.forEach(pl -> pl.updateValue(pl.request()));
|
||||
}
|
||||
|
||||
public boolean shouldSee(TabPlayer viewer, TabPlayer displayed) {
|
||||
@@ -83,103 +77,78 @@ public class GlobalPlayerList extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
List<PlayerInfoData> entries = new ArrayList<>();
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all == connectedPlayer) continue;
|
||||
if (connectedPlayer.getServer().equals(all.getServer())) continue;
|
||||
if (shouldSee(all, connectedPlayer)) {
|
||||
all.sendCustomPacket(getAddPacket(connectedPlayer, all), this);
|
||||
}
|
||||
if (shouldSee(connectedPlayer, all)) {
|
||||
connectedPlayer.sendCustomPacket(getAddPacket(all, connectedPlayer), this);
|
||||
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, getAddInfoData(connectedPlayer, all)), this);
|
||||
}
|
||||
if (shouldSee(connectedPlayer, all)) entries.add(getAddInfoData(all, connectedPlayer));
|
||||
}
|
||||
placeholders.forEach(pl -> pl.updateValue(pl.request()));
|
||||
if (!entries.isEmpty()) connectedPlayer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, entries), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
//delay due to waterfall bug calling server switch when players leave
|
||||
TAB.getInstance().getCPUManager().runTaskLater(50, this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> {
|
||||
|
||||
if (TAB.getInstance().getPlayer(disconnectedPlayer.getName()) != null) return;
|
||||
PacketPlayOutPlayerInfo remove = getRemovePacket(disconnectedPlayer);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all == disconnectedPlayer) continue;
|
||||
all.sendCustomPacket(remove, this);
|
||||
}
|
||||
//update after player is removed
|
||||
placeholders.forEach(pl -> pl.updateValue(pl.request()));
|
||||
});
|
||||
PacketPlayOutPlayerInfo remove = getRemovePacket(disconnectedPlayer);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all == disconnectedPlayer) continue;
|
||||
all.sendCustomPacket(remove, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerChange(TabPlayer p, String from, String to) {
|
||||
lastServerSwitch.put(p, System.currentTimeMillis());
|
||||
Runnable r = () -> {
|
||||
PacketPlayOutPlayerInfo removeChanged = getRemovePacket(p);
|
||||
public void onServerChange(TabPlayer changed, String from, String to) {
|
||||
// Event is fired after all entries are removed from switched player's tablist, ready to re-add immediately
|
||||
List<PlayerInfoData> entries = new ArrayList<>();
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all == changed) continue;
|
||||
if (shouldSee(changed, all)) {
|
||||
entries.add(getAddInfoData(all, changed));
|
||||
} else {
|
||||
changed.sendCustomPacket(getRemovePacket(all), this);
|
||||
}
|
||||
}
|
||||
if (!entries.isEmpty()) changed.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, entries), this);
|
||||
|
||||
// Player who switched server is removed from tablist of other players in ~70-110ms (depending on online count), re-add with a delay
|
||||
TAB.getInstance().getCPUManager().runTaskLater(200, this, TabConstants.CpuUsageCategory.SERVER_SWITCH, () -> {
|
||||
PacketPlayOutPlayerInfo removeChanged = getRemovePacket(changed);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all == p) continue;
|
||||
if (shouldSee(all, p)) {
|
||||
all.sendCustomPacket(getAddPacket(p, all), this);
|
||||
if (all == changed) continue;
|
||||
if (shouldSee(all, changed)) {
|
||||
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, getAddInfoData(changed, all)), this);
|
||||
} else {
|
||||
all.sendCustomPacket(removeChanged, this);
|
||||
}
|
||||
if (shouldSee(p, all)) {
|
||||
p.sendCustomPacket(getAddPacket(all, p), this);
|
||||
} else {
|
||||
p.sendCustomPacket(getRemovePacket(all), this);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) {
|
||||
TAB.getInstance().getCPUManager().runTaskLater(200, this, TabConstants.CpuUsageCategory.SERVER_SWITCH, r);
|
||||
} else {
|
||||
r.run();
|
||||
}
|
||||
placeholders.forEach(pl -> pl.updateValue(pl.request()));
|
||||
});
|
||||
}
|
||||
|
||||
public PacketPlayOutPlayerInfo getRemovePacket(TabPlayer p) {
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
new PlayerInfoData(p.getTablistId()), new PlayerInfoData(EMPTY_ID));
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new PlayerInfoData(p.getTablistId()));
|
||||
}
|
||||
|
||||
public PacketPlayOutPlayerInfo getAddPacket(TabPlayer p, TabPlayer viewer) {
|
||||
public PlayerInfoData getAddInfoData(TabPlayer p, TabPlayer viewer) {
|
||||
IChatBaseComponent format = null;
|
||||
PlayerList playerlist = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
if (playerlist != null) {
|
||||
format = playerlist.getTabFormat(p, viewer);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER,
|
||||
new PlayerInfoData(
|
||||
p.getName(),
|
||||
p.getTablistId(),
|
||||
p.getSkin(),
|
||||
true,
|
||||
p.getPing(),
|
||||
vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE,
|
||||
viewer.getVersion().getMinorVersion() >= 8 ? format : null,
|
||||
fillProfileKey ? ((ProxyTabPlayer)p).getChatSessionId() : null,
|
||||
fillProfileKey ? ((ProxyTabPlayer)p).getProfilePublicKey() : null
|
||||
)
|
||||
return new PlayerInfoData(
|
||||
p.getName(),
|
||||
p.getTablistId(),
|
||||
p.getSkin(),
|
||||
true,
|
||||
p.getPing(),
|
||||
vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE,
|
||||
viewer.getVersion().getMinorVersion() >= 8 ? format : null,
|
||||
fillProfileKey ? ((ProxyTabPlayer)p).getChatSessionId() : null,
|
||||
fillProfileKey ? ((ProxyTabPlayer)p).getProfilePublicKey() : null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
if (info.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
boolean packetFromTAB = info.getEntries().stream().anyMatch(data -> data.getUniqueId().equals(EMPTY_ID));
|
||||
for (PlayerInfoData playerInfoData : info.getEntries()) {
|
||||
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(playerInfoData.getUniqueId());
|
||||
//not preventing NPC removals
|
||||
if (packetPlayer != null && !packetFromTAB && !packetPlayer.isVanished()
|
||||
&& (System.currentTimeMillis()-lastServerSwitch.getOrDefault(packetPlayer, 0L) < 2000)
|
||||
) {
|
||||
//remove packet not coming from tab
|
||||
//changing to random non-existing player, the easiest way to cancel the removal
|
||||
playerInfoData.setUniqueId(UUID.randomUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!displayAsSpectators) return;
|
||||
if (info.getActions().contains(EnumPlayerInfoAction.UPDATE_GAME_MODE)) {
|
||||
for (PlayerInfoData playerInfoData : info.getEntries()) {
|
||||
@@ -204,7 +173,7 @@ public class GlobalPlayerList extends TabFeature {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer == p) continue;
|
||||
if (shouldSee(viewer, p)) {
|
||||
viewer.sendCustomPacket(getAddPacket(p, viewer), TabConstants.PacketCategory.GLOBAL_PLAYERLIST_VANISH);
|
||||
viewer.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, getAddInfoData(p, viewer)), TabConstants.PacketCategory.GLOBAL_PLAYERLIST_VANISH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.shared.features.globalplayerlist;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
@@ -10,8 +11,10 @@ import me.neznamy.tab.shared.TAB;
|
||||
|
||||
public class LatencyRefresher extends TabFeature {
|
||||
|
||||
public LatencyRefresher() {
|
||||
super("Global PlayerList", "Updating latency");
|
||||
@Getter private final String featureName = "Global PlayerList";
|
||||
@Getter private final String refreshDisplayName = "Updating latency";
|
||||
|
||||
{
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder(TabConstants.Placeholder.PING, this);
|
||||
}
|
||||
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package me.neznamy.tab.shared.features.injection;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Function;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
* A pipeline injector for Netty connections. As most servers use Netty, this avoids code duplication.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public abstract class NettyPipelineInjector extends PipelineInjector {
|
||||
|
||||
//handler to inject before
|
||||
private final String injectPosition;
|
||||
|
||||
protected abstract Channel getChannel(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Injects custom channel duplex handler to prevent other plugins from overriding this one
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
@Override
|
||||
public void inject(TabPlayer player) {
|
||||
final Channel channel = getChannel(player);
|
||||
if (player.getVersion().getMinorVersion() < 8 || channel == null) return; //hello A248
|
||||
if (!channel.pipeline().names().contains(injectPosition)) {
|
||||
//fake player or waterfall bug
|
||||
return;
|
||||
}
|
||||
uninject(player);
|
||||
try {
|
||||
channel.pipeline().addBefore(injectPosition, TabConstants.PIPELINE_HANDLER_NAME, getChannelFunction().apply(player));
|
||||
} catch (NoSuchElementException | IllegalArgumentException e) {
|
||||
//I don't really know how does this keep happening but whatever
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject(TabPlayer player) {
|
||||
final Channel channel = getChannel(player);
|
||||
if (player.getVersion().getMinorVersion() < 8 || channel == null) return; //hello A248
|
||||
try {
|
||||
if (channel.pipeline().names().contains(TabConstants.PIPELINE_HANDLER_NAME)) channel.pipeline().remove(TabConstants.PIPELINE_HANDLER_NAME);
|
||||
} catch (NoSuchElementException e) {
|
||||
//for whatever reason this rarely throws
|
||||
//java.util.NoSuchElementException: TABReader
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Function<TabPlayer, ChannelDuplexHandler> getChannelFunction();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.neznamy.tab.shared.features.injection;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* Packet intercepting to secure proper functionality of some features:
|
||||
* TabList names - anti-override
|
||||
* NameTags - anti-override
|
||||
* Scoreboard - disabling tab's scoreboard to prevent conflict
|
||||
* SpectatorFix - to change game mode to something else than spectator
|
||||
* PetFix - to remove owner field from entity data
|
||||
* PingSpoof - full feature functionality
|
||||
* Unlimited name tags - replacement for bukkit events with much better accuracy and reliability
|
||||
*/
|
||||
public abstract class PipelineInjector extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Pipeline injection";
|
||||
|
||||
//preventing spam when packet is sent to everyone
|
||||
private String lastTeamOverrideMessage;
|
||||
|
||||
//anti-override rules
|
||||
protected boolean antiOverrideTeams;
|
||||
protected boolean byteBufDeserialization;
|
||||
|
||||
public abstract void inject(TabPlayer player);
|
||||
|
||||
public abstract void uninject(TabPlayer player);
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
antiOverrideTeams = TAB.getInstance().getConfig().getBoolean("scoreboard-teams.enabled", true) &&
|
||||
TAB.getInstance().getConfig().getBoolean("scoreboard-teams.anti-override", true);
|
||||
boolean respectOtherScoreboardPlugins = TAB.getInstance().getConfig().getBoolean("scoreboard.enabled", false) &&
|
||||
TAB.getInstance().getConfig().getBoolean("scoreboard.respect-other-plugins", true);
|
||||
byteBufDeserialization = antiOverrideTeams || respectOtherScoreboardPlugins;
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
inject(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
uninject(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
inject(connectedPlayer);
|
||||
}
|
||||
|
||||
protected void logTeamOverride(String team, String player, String expectedTeam) {
|
||||
String message = "Something just tried to add player " + player + " into team " + team + " (expected team: " + expectedTeam + ")";
|
||||
//not logging the same message for every online player who received the packet
|
||||
if (!message.equals(lastTeamOverrideMessage)) {
|
||||
lastTeamOverrideMessage = message;
|
||||
TAB.getInstance().getErrorManager().printError(message, null, false, TAB.getInstance().getErrorManager().getAntiOverrideLog());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.features.layout;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
@@ -12,28 +13,38 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class FixedSlot extends TabFeature {
|
||||
|
||||
private final Layout layout;
|
||||
@Getter private final UUID id;
|
||||
@Getter private final String text;
|
||||
@Getter private final String propertyName;
|
||||
@Getter private final Skin skin;
|
||||
@Getter private final int ping;
|
||||
@Getter private final String featureName = "Layout";
|
||||
@Getter private final String refreshDisplayName = "Updating fixed slots";
|
||||
|
||||
public FixedSlot(Layout layout, int slot, String text, String skin, int ping) {
|
||||
super(layout.getFeatureName(), "Updating fixed slots");
|
||||
this.layout = layout;
|
||||
this.id = layout.getManager().getUUID(slot);
|
||||
this.text = text;
|
||||
propertyName = "Layout-" + layout.getName() + "SLOT-" + slot;
|
||||
this.skin = layout.getManager().getSkinManager().getSkin(skin.length() == 0 ? layout.getManager().getDefaultSkin() : skin);
|
||||
this.ping = ping;
|
||||
}
|
||||
private final Layout layout;
|
||||
private final UUID id;
|
||||
private final String text;
|
||||
private final String propertyName;
|
||||
private final Skin skin;
|
||||
private final int ping;
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer p, boolean force) {
|
||||
if (!layout.containsViewer(p) || p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, new PlayerInfoData(id, IChatBaseComponent.optimizedComponent(p.getProperty(propertyName).updateAndGet()))), TabConstants.PacketCategory.LAYOUT_FIXED_SLOTS);
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
|
||||
new PlayerInfoData(id, IChatBaseComponent.optimizedComponent(p.getProperty(propertyName).updateAndGet()))), TabConstants.PacketCategory.LAYOUT_FIXED_SLOTS);
|
||||
}
|
||||
|
||||
public PlayerInfoData createEntry(TabPlayer viewer) {
|
||||
viewer.setProperty(this, propertyName, text);
|
||||
return new PlayerInfoData(
|
||||
layout.getEntryName(viewer, id.getLeastSignificantBits()),
|
||||
id,
|
||||
skin,
|
||||
true,
|
||||
ping,
|
||||
PacketPlayOutPlayerInfo.EnumGamemode.CREATIVE,
|
||||
IChatBaseComponent.optimizedComponent(viewer.getProperty(propertyName).updateAndGet()), // maybe just get is fine?
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package me.neznamy.tab.shared.features.layout;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -13,12 +10,17 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.placeholders.conditions.Condition;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class Layout extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Layout";
|
||||
@Getter private final String refreshDisplayName = "Updating player groups";
|
||||
@Getter private final String name;
|
||||
@Getter private final LayoutManager manager;
|
||||
private final Condition displayCondition;
|
||||
@@ -27,24 +29,13 @@ public class Layout extends TabFeature {
|
||||
@Getter private final List<ParentGroup> groups;
|
||||
@Getter private final Set<TabPlayer> viewers = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
public Layout(String name, LayoutManager manager, Condition displayCondition, Map<Integer, FixedSlot> fixedSlots, List<Integer> emptySlots, List<ParentGroup> groups) {
|
||||
super(manager.getFeatureName(), "Updating player groups");
|
||||
this.name = name;
|
||||
this.manager = manager;
|
||||
this.displayCondition = displayCondition;
|
||||
this.fixedSlots = fixedSlots;
|
||||
this.emptySlots = emptySlots;
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public void sendTo(TabPlayer p) {
|
||||
if (viewers.contains(p)) return;
|
||||
viewers.add(p);
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
groups.forEach(g -> list.addAll(g.getSlots(p)));
|
||||
for (FixedSlot slot : fixedSlots.values()) {
|
||||
p.setProperty(slot, slot.getPropertyName(), slot.getText());
|
||||
list.add(new PlayerInfoData(getEntryName(p, slot.getId().getLeastSignificantBits()), slot.getId(), slot.getSkin(), true, slot.getPing(), EnumGamemode.CREATIVE, IChatBaseComponent.optimizedComponent(p.getProperty(slot.getPropertyName()).updateAndGet()), null, null));
|
||||
list.add(slot.createEntry(p));
|
||||
}
|
||||
for (int slot : emptySlots) {
|
||||
list.add(new PlayerInfoData(getEntryName(p, slot), manager.getUUID(slot), manager.getSkinManager().getDefaultSkin(), true, manager.getEmptySlotPing(), EnumGamemode.CREATIVE, new IChatBaseComponent(""), null, null));
|
||||
@@ -60,12 +51,8 @@ public class Layout extends TabFeature {
|
||||
public void removeFrom(TabPlayer p) {
|
||||
if (!viewers.contains(p)) return;
|
||||
viewers.remove(p);
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (UUID id : manager.getUuids().values()) {
|
||||
list.add(new PlayerInfoData(id));
|
||||
}
|
||||
if (p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, list), this);
|
||||
p.sendCustomPacket(manager.getRemovePacket(), this);
|
||||
}
|
||||
|
||||
public boolean isConditionMet(TabPlayer p) {
|
||||
@@ -100,13 +87,4 @@ public class Layout extends TabFeature {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerChange(TabPlayer player, String from, String to) {
|
||||
if (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) return;
|
||||
//velocity clearing TabList on server switch
|
||||
if (viewers.remove(player)) {
|
||||
sendTo(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,18 +1,21 @@
|
||||
package me.neznamy.tab.shared.features.layout;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LayoutLatencyRefresher extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "Layout";
|
||||
@Getter private final String refreshDisplayName = "Updating latency";
|
||||
private final LayoutManager manager;
|
||||
|
||||
public LayoutLatencyRefresher(LayoutManager manager) {
|
||||
super(manager.getFeatureName(), "Updating latency");
|
||||
this.manager = manager;
|
||||
{
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder(TabConstants.Placeholder.PING, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.features.layout;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
@@ -12,6 +13,7 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PlayerList;
|
||||
import me.neznamy.tab.shared.features.layout.skin.SkinManager;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.placeholders.conditions.Condition;
|
||||
@@ -30,17 +32,25 @@ public class LayoutManager extends TabFeature {
|
||||
put(slot, new UUID(0, translateSlot(slot)));
|
||||
}
|
||||
}};
|
||||
@Getter private final PacketPlayOutPlayerInfo removePacket = new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
uuids.values().stream().map(PlayerInfoData::new).collect(Collectors.toList()));
|
||||
private final Map<String, Layout> layouts = loadLayouts();
|
||||
@Getter private final WeakHashMap<TabPlayer, Layout> playerViews = new WeakHashMap<>();
|
||||
private final WeakHashMap<TabPlayer, String> teamNames = new WeakHashMap<>();
|
||||
@Getter private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(teamNames::get)));
|
||||
private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
|
||||
@Getter private PlayerList playerList;
|
||||
@Getter private final String featureName = "Layout";
|
||||
@Getter private final String refreshDisplayName = "Switching layouts";
|
||||
|
||||
public LayoutManager() {
|
||||
super("Layout", "Switching layouts");
|
||||
@Override
|
||||
public void load() {
|
||||
playerList = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.LAYOUT_LATENCY, new LayoutLatencyRefresher(this));
|
||||
TAB.getInstance().debug("Loaded Layout feature");
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
onJoin(p);
|
||||
}
|
||||
}
|
||||
|
||||
private Direction parseDirection(String value) {
|
||||
@@ -73,7 +83,9 @@ public class LayoutManager extends TabFeature {
|
||||
String text = array[1];
|
||||
String skin = array.length > 2 ? array[2] : "";
|
||||
int ping = array.length > 3 ? TAB.getInstance().getErrorManager().parseInteger(array[3], emptySlotPing) : emptySlotPing;
|
||||
FixedSlot f = new FixedSlot(l, slot, text, skin, ping);
|
||||
FixedSlot f = new FixedSlot(l, uuids.get(slot), text,
|
||||
"Layout-" + l.getName() + "SLOT-" + slot,
|
||||
skinManager.getSkin(skin.length() == 0 ? defaultSkin : skin), ping);
|
||||
fixedSlots.put(slot, f);
|
||||
emptySlots.remove((Integer)slot);
|
||||
if (text.length() > 0) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layoutSlot(layout.getKey().toString(), slot), f);
|
||||
@@ -94,8 +106,12 @@ public class LayoutManager extends TabFeature {
|
||||
positions.add(i);
|
||||
}
|
||||
}
|
||||
positions.removeAll(fixedSlots.keySet());
|
||||
parentGroups.add(new ParentGroup(l, condition, positions.stream().mapToInt(i->i).toArray()));
|
||||
emptySlots.removeAll(positions);
|
||||
if (condition != null) {
|
||||
l.addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(condition.getName())));
|
||||
}
|
||||
}
|
||||
}
|
||||
layoutMap.put(layout.getKey().toString(), l);
|
||||
@@ -137,13 +153,6 @@ public class LayoutManager extends TabFeature {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
onJoin(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer p, boolean force) {
|
||||
Layout highest = getHighestLayout(p);
|
||||
@@ -157,13 +166,9 @@ public class LayoutManager extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
List<PlayerInfoData> list = new ArrayList<>();
|
||||
for (UUID id : uuids.values()) {
|
||||
list.add(new PlayerInfoData(id));
|
||||
}
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) continue;
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, list), this);
|
||||
p.sendCustomPacket(removePacket, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.*;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.placeholders.conditions.Condition;
|
||||
|
||||
public class ParentGroup {
|
||||
@@ -19,9 +18,6 @@ public class ParentGroup {
|
||||
public ParentGroup(Layout layout, Condition condition, int[] slots) {
|
||||
this.layout = layout;
|
||||
this.condition = condition;
|
||||
if (condition != null) {
|
||||
layout.addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(condition.getName())));
|
||||
}
|
||||
this.slots = slots;
|
||||
for (int slot : slots) {
|
||||
playerSlots.put(slot, new PlayerSlot(layout, layout.getManager().getUUID(slot)));
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
package me.neznamy.tab.shared.features.layout;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PlayerList;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PlayerSlot {
|
||||
|
||||
private final PlayerList playerlist = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
private final Layout layout;
|
||||
@Getter private final UUID uniqueId;
|
||||
@Getter private TabPlayer player;
|
||||
@@ -39,9 +37,28 @@ public class PlayerSlot {
|
||||
PlayerInfoData data;
|
||||
TabPlayer player = this.player; //avoiding NPE from concurrent access
|
||||
if (player != null) {
|
||||
data = new PlayerInfoData(layout.getEntryName(p, uniqueId.getLeastSignificantBits()), uniqueId, player.getSkin(), true, player.getPing(), EnumGamemode.SURVIVAL, playerlist == null ? new IChatBaseComponent(player.getName()) : playerlist.getTabFormat(player, p), null, null);
|
||||
PlayerList playerList = layout.getManager().getPlayerList();
|
||||
data = new PlayerInfoData(
|
||||
layout.getEntryName(p, uniqueId.getLeastSignificantBits()),
|
||||
uniqueId,
|
||||
player.getSkin(),
|
||||
true,
|
||||
player.getPing(),
|
||||
EnumGamemode.SURVIVAL,
|
||||
playerList == null ? new IChatBaseComponent(player.getName()) : playerList.getTabFormat(player, p),
|
||||
null,
|
||||
null);
|
||||
} else {
|
||||
data = new PlayerInfoData(layout.getEntryName(p, uniqueId.getLeastSignificantBits()), uniqueId, layout.getManager().getSkinManager().getDefaultSkin(), true, layout.getManager().getEmptySlotPing(), EnumGamemode.SURVIVAL, new IChatBaseComponent(text), null, null);
|
||||
data = new PlayerInfoData(
|
||||
layout.getEntryName(p, uniqueId.getLeastSignificantBits()),
|
||||
uniqueId,
|
||||
layout.getManager().getSkinManager().getDefaultSkin(),
|
||||
true,
|
||||
layout.getManager().getEmptySlotPing(),
|
||||
EnumGamemode.SURVIVAL,
|
||||
new IChatBaseComponent(text),
|
||||
null,
|
||||
null);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
+12
-13
@@ -3,23 +3,30 @@ package me.neznamy.tab.shared.features.nametags;
|
||||
import java.util.Collections;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CollisionManager extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "NameTags";
|
||||
@Getter private final String refreshDisplayName = "Updating collision";
|
||||
private final NameTag nameTags;
|
||||
private final boolean collisionRule;
|
||||
private final WeakHashMap<TabPlayer, Boolean> collision = new WeakHashMap<>();
|
||||
private final WeakHashMap<TabPlayer, Boolean> forcedCollision = new WeakHashMap<>();
|
||||
|
||||
public CollisionManager(NameTag nameTags, boolean collisionRule) {
|
||||
super(nameTags.getFeatureName(), "Updating collision");
|
||||
this.nameTags = nameTags;
|
||||
this.collisionRule = collisionRule;
|
||||
public boolean getCollision(TabPlayer p) {
|
||||
return forcedCollision.getOrDefault(p, collision.getOrDefault(p, collisionRule));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() < 9) return; //cannot control collision anyway
|
||||
if (!collisionRule) return; //no need to refresh disguise status since collision is disabled
|
||||
if (TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.LIBS_DISGUISES) == null && TAB.getInstance().getServerVersion() != ProtocolVersion.PROXY) return; //no disguise plugin available
|
||||
@@ -31,16 +38,8 @@ public class CollisionManager extends TabFeature {
|
||||
return newCollision;
|
||||
});
|
||||
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.COLLISION));
|
||||
}
|
||||
|
||||
public boolean getCollision(TabPlayer p) {
|
||||
return forcedCollision.getOrDefault(p, collision.getOrDefault(p, collisionRule));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
collision.put(all, collisionRule);
|
||||
collision.put(all, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
package me.neznamy.tab.shared.features.nametags;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.api.team.TeamManager;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class NameTag extends TabFeature implements TeamManager {
|
||||
|
||||
@Getter private final String featureName = "NameTags";
|
||||
@Getter private final String refreshDisplayName = "Updating prefix/suffix";
|
||||
protected final boolean invisibleNameTags = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.invisible-nametags", false);
|
||||
private final boolean collisionRule = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.enable-collision", true);
|
||||
private final boolean canSeeFriendlyInvisibles = TAB.getInstance().getConfig().getBoolean("scoreboard-teams.can-see-friendly-invisibles", false);
|
||||
@@ -33,29 +31,23 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
private final WeakHashMap<TabPlayer, String> forcedTeamName = new WeakHashMap<>();
|
||||
protected final Set<TabPlayer> playersWithInvisibleNameTagView = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
private RedisSupport redis;
|
||||
|
||||
private final boolean accepting18x = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ||
|
||||
TAB.getInstance().getPlatform().getPluginVersion("ViaRewind") != null ||
|
||||
TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.VIAREWIND) != null ||
|
||||
TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.PROTOCOL_SUPPORT) != null ||
|
||||
TAB.getInstance().getServerVersion().getMinorVersion() == 8;
|
||||
|
||||
public NameTag() {
|
||||
super("NameTags", "Updating prefix/suffix", "scoreboard-teams");
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.SORTING, sorting);
|
||||
if (accepting18x) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_VISIBILITY, new VisibilityRefresher(this));
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_COLLISION, collisionManager);
|
||||
PipelineInjector pipeline = (PipelineInjector) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PIPELINE_INJECTION);
|
||||
boolean antiOverride = TAB.getInstance().getConfig().getBoolean("scoreboard-teams.anti-override", true);
|
||||
if (pipeline != null && antiOverride) {
|
||||
pipeline.setAntiOverrideTeams(true);
|
||||
pipeline.setByteBufDeserialization(true);
|
||||
}
|
||||
TAB.getInstance().debug(String.format("Loaded NameTag feature with parameters collisionRule=%s, disabledWorlds=%s, disabledServers=%s, invisibleNameTags=%s",
|
||||
collisionRule, Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), invisibleNameTags));
|
||||
super("scoreboard-teams");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
// RedisSupport is instantiated after NameTags, so must be loaded after
|
||||
redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (accepting18x) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_VISIBILITY, new VisibilityRefresher(this));
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_COLLISION, collisionManager);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
updateProperties(all);
|
||||
hiddenNameTagFor.put(all, new ArrayList<>());
|
||||
@@ -63,15 +55,26 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
addDisabledPlayer(all);
|
||||
continue;
|
||||
}
|
||||
registerTeam(all);
|
||||
if (expansion != null) expansion.setNameTagVisibility(all, true);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(all, true);
|
||||
}
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (!isDisabledPlayer(target)) registerTeam(target, viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (!isDisabledPlayer(p)) unregisterTeam(p, sorting.getShortTeamName(p));
|
||||
List<PacketPlayOutScoreboardTeam> packets = new ArrayList<>();
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (hasTeamHandlingPaused(target)) return;
|
||||
packets.add(new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(target)));
|
||||
}
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (PacketPlayOutScoreboardTeam packet : packets) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +98,7 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
boolean suffix = refreshed.getProperty(TabConstants.Property.TAGSUFFIX).update();
|
||||
refresh = prefix || suffix;
|
||||
}
|
||||
if (refresh) updateTeam(refreshed);
|
||||
if (refresh) updateTeamData(refreshed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,10 +112,7 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
registerTeam(all, connectedPlayer);
|
||||
}
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) {
|
||||
expansion.setNameTagVisibility(connectedPlayer, true);
|
||||
}
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(connectedPlayer, true);
|
||||
if (isDisabled(connectedPlayer.getServer(), connectedPlayer.getWorld())) {
|
||||
addDisabledPlayer(connectedPlayer);
|
||||
return;
|
||||
@@ -157,7 +157,7 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
} else if (!disabledNow && disabledBefore) {
|
||||
registerTeam(p);
|
||||
} else if (changed) {
|
||||
updateTeam(p);
|
||||
updateTeamData(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,6 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
forcedTeamName.put(player, name);
|
||||
registerTeam(player);
|
||||
if (name != null) sorting.setTeamNameNote(player, "Set using API");
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateTeamName(player, sorting.getShortTeamName(player));
|
||||
}
|
||||
|
||||
@@ -247,15 +246,9 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
|
||||
@Override
|
||||
public void updateTeamData(TabPlayer p) {
|
||||
Property tagPrefix = p.getProperty(TabConstants.Property.TAGPREFIX);
|
||||
Property tagSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
String currentPrefix = tagPrefix.getFormat(viewer);
|
||||
String currentSuffix = tagSuffix.getFormat(viewer);
|
||||
boolean visible = getTeamVisibility(p, viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(p), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
|
||||
updateTeamData(p, viewer);
|
||||
}
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateNameTag(p, p.getProperty(TabConstants.Property.TAGPREFIX).get(), p.getProperty(TabConstants.Property.TAGSUFFIX).get());
|
||||
}
|
||||
|
||||
@@ -267,7 +260,7 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
}
|
||||
|
||||
public void unregisterTeam(TabPlayer p, String teamName) {
|
||||
if (hasTeamHandlingPaused(p) || sorting.getShortTeamName(p) == null) return;
|
||||
if (hasTeamHandlingPaused(p)) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName), TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
|
||||
}
|
||||
@@ -287,21 +280,6 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
translate(collisionManager.getCollision(p)), Collections.singletonList(p.getNickname()), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_REGISTER);
|
||||
}
|
||||
|
||||
private void updateTeam(TabPlayer p) {
|
||||
String oldName = getSorting().getShortTeamName(p);
|
||||
getSorting().constructTeamNames(p);
|
||||
if (oldName.equals(sorting.getShortTeamName(p))) {
|
||||
updateTeamData(p);
|
||||
} else {
|
||||
unregisterTeam(p, oldName);
|
||||
LayoutManager layout = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
|
||||
if (layout != null) layout.updateTeamName(p, sorting.getFullTeamName(p));
|
||||
registerTeam(p);
|
||||
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) redis.updateTeamName(p, sorting.getShortTeamName(p));
|
||||
}
|
||||
}
|
||||
|
||||
public String translate(boolean b) {
|
||||
return b ? "always" : "never";
|
||||
}
|
||||
@@ -378,10 +356,7 @@ public class NameTag extends TabFeature implements TeamManager {
|
||||
playersWithInvisibleNameTagView.add(player);
|
||||
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNameTagsHidden(), true);
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) {
|
||||
expansion.setNameTagVisibility(player, !playersWithInvisibleNameTagView.contains(player));
|
||||
}
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(player, !playersWithInvisibleNameTagView.contains(player));
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
updateTeamData(all, player);
|
||||
}
|
||||
|
||||
+7
-3
@@ -2,24 +2,28 @@ package me.neznamy.tab.shared.features.nametags;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class VisibilityRefresher extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "NameTags";
|
||||
@Getter private final String refreshDisplayName = "Updating NameTag visibility";
|
||||
private final NameTag nameTags;
|
||||
|
||||
public VisibilityRefresher(NameTag nameTags) {
|
||||
super(nameTags.getFeatureName(), "Updating NameTag visibility");
|
||||
this.nameTags = nameTags;
|
||||
{
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.INVISIBLE, 500, TabPlayer::hasInvisibilityPotion);
|
||||
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.INVISIBLE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer p, boolean force) {
|
||||
if (nameTags.isDisabledPlayer(p)) return;
|
||||
nameTags.updateTeamData(p);
|
||||
}
|
||||
}
|
||||
+11
-19
@@ -2,12 +2,11 @@ package me.neznamy.tab.shared.features.nametags.unlimited;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.team.UnlimitedNametagManager;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
|
||||
import java.util.*;
|
||||
@@ -37,14 +36,9 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
public NameTagX(BiFunction<NameTagX, TabPlayer, ArmorStandManager> armorStandFunction) {
|
||||
this.armorStandFunction = armorStandFunction;
|
||||
}
|
||||
{
|
||||
Collections.reverse(dynamicLines);
|
||||
if (invisibleNameTags) {
|
||||
TAB.getInstance().getErrorManager().startupWarn("Unlimited nametag mode is enabled as well as invisible nametags. These 2 options are mutually exclusive.");
|
||||
TAB.getInstance().getErrorManager().startupWarn("If you want nametags to be invisible, you don't need unlimited nametag mode at all.");
|
||||
TAB.getInstance().getErrorManager().startupWarn("If you want enhanced nametags without limits, making them invisible would defeat the purpose.");
|
||||
}
|
||||
TAB.getInstance().debug(String.format("Loaded Unlimited NameTag feature with parameters markerFor18x=%s, disableOnBoats=%s, disabledUnlimitedServers=%s, disabledUnlimitedWorlds=%s",
|
||||
markerFor18x, disableOnBoats, disabledUnlimitedServers, disabledUnlimitedWorlds));
|
||||
}
|
||||
|
||||
public boolean isUnlimitedDisabled(String server, String world) {
|
||||
@@ -66,14 +60,18 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (invisibleNameTags) {
|
||||
TAB.getInstance().getErrorManager().startupWarn("Unlimited nametag mode is enabled as well as invisible nametags. These 2 options are mutually exclusive.");
|
||||
TAB.getInstance().getErrorManager().startupWarn("If you want nametags to be invisible, you don't need unlimited nametag mode at all.");
|
||||
TAB.getInstance().getErrorManager().startupWarn("If you want enhanced nametags without limits, making them invisible would defeat the purpose.");
|
||||
}
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
updateProperties(all);
|
||||
armorStandManagerMap.put(all, armorStandFunction.apply(this, all));
|
||||
if (isUnlimitedDisabled(all.getServer(), all.getWorld())) {
|
||||
disabledUnlimitedPlayers.add(all);
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setNameTagPreview(all, false);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(all, false);
|
||||
}
|
||||
super.load();
|
||||
}
|
||||
@@ -84,8 +82,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
disabledUnlimitedPlayers.add(connectedPlayer);
|
||||
super.onJoin(connectedPlayer);
|
||||
armorStandManagerMap.put(connectedPlayer, armorStandFunction.apply(this, connectedPlayer));
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setNameTagPreview(connectedPlayer, false);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(connectedPlayer, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,8 +110,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNametagPreviewOn(), true);
|
||||
playersPreviewingNametag.add(player);
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setNameTagPreview(player, isPreviewingNametag(player));
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(player, isPreviewingNametag(player));
|
||||
}
|
||||
|
||||
public boolean isPreviewingNametag(TabPlayer player) {
|
||||
@@ -286,7 +282,6 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
@Override
|
||||
public void setPrefix(TabPlayer player, String prefix) {
|
||||
Preconditions.checkLoaded(player);
|
||||
super.setPrefix(player, prefix);
|
||||
rebuildNameTagLine(player);
|
||||
getArmorStandManager(player).refresh(true);
|
||||
@@ -294,7 +289,6 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
@Override
|
||||
public void setSuffix(TabPlayer player, String suffix) {
|
||||
Preconditions.checkLoaded(player);
|
||||
super.setSuffix(player, suffix);
|
||||
rebuildNameTagLine(player);
|
||||
getArmorStandManager(player).refresh(true);
|
||||
@@ -302,7 +296,6 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
@Override
|
||||
public void resetPrefix(TabPlayer player) {
|
||||
Preconditions.checkLoaded(player);
|
||||
super.resetPrefix(player);
|
||||
rebuildNameTagLine(player);
|
||||
getArmorStandManager(player).refresh(true);
|
||||
@@ -310,7 +303,6 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
|
||||
|
||||
@Override
|
||||
public void resetSuffix(TabPlayer player) {
|
||||
Preconditions.checkLoaded(player);
|
||||
super.resetSuffix(player);
|
||||
rebuildNameTagLine(player);
|
||||
getArmorStandManager(player).refresh(true);
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package me.neznamy.tab.shared.features.redis;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.event.EventHandler;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.event.impl.TabPlaceholderRegisterEvent;
|
||||
import me.neznamy.tab.shared.features.PlayerList;
|
||||
import me.neznamy.tab.shared.features.globalplayerlist.GlobalPlayerList;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.placeholders.ServerPlaceholderImpl;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
@@ -26,40 +30,25 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class RedisSupport extends TabFeature {
|
||||
|
||||
@Getter private final String featureName = "RedisSupport";
|
||||
|
||||
/** Redis players on other proxies by their UUID */
|
||||
@Getter protected final Map<String, RedisPlayer> redisPlayers = new ConcurrentHashMap<>();
|
||||
|
||||
/** UUID of this proxy to ignore messages coming from the same proxy */
|
||||
protected final UUID proxy = UUID.randomUUID();
|
||||
|
||||
/** Global PlayerList feature */
|
||||
/** Features this one hooks into */
|
||||
protected final GlobalPlayerList global = (GlobalPlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST);
|
||||
|
||||
/** PlayerList feature */
|
||||
@Getter private final PlayerList playerList = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
|
||||
/** NameTag feature */
|
||||
@Getter private final NameTag nameTags = (NameTag) TAB.getInstance().getTeamManager();
|
||||
|
||||
/** Sorting feature */
|
||||
@Getter private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
|
||||
@Getter private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
|
||||
|
||||
private final UUID EMPTY_ID = new UUID(0, 0);
|
||||
|
||||
private final Map<RedisPlayer, Long> lastServerSwitch = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
protected RedisSupport() {
|
||||
super("RedisBungee", null);
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.ONLINE, 1000, () ->
|
||||
Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(all -> !all.isVanished()).count() +
|
||||
redisPlayers.values().stream().filter(all -> !all.isVanished()).count());
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.STAFF_ONLINE, 1000, () ->
|
||||
Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(all -> !all.isVanished() && all.hasPermission(TabConstants.Permission.STAFF)).count() +
|
||||
redisPlayers.values().stream().filter(all -> !all.isVanished() && all.isStaff()).count());
|
||||
}
|
||||
private EventHandler<TabPlaceholderRegisterEvent> eventHandler;
|
||||
|
||||
/**
|
||||
* Sends a message to all other proxies to update
|
||||
@@ -333,6 +322,23 @@ public abstract class RedisSupport extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
eventHandler = event -> {
|
||||
String identifier = event.getIdentifier();
|
||||
if (identifier.startsWith("%online_")) {
|
||||
String server = identifier.substring(8, identifier.length()-1);
|
||||
event.setPlaceholder(new ServerPlaceholderImpl(identifier, 1000, () ->
|
||||
Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(p -> p.getServer().equals(server) && !p.isVanished()).count() +
|
||||
redisPlayers.values().stream().filter(all -> all.getServer().equals(server) && !all.isVanished()).count()));
|
||||
|
||||
}
|
||||
};
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.ONLINE, 1000, () ->
|
||||
Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(all -> !all.isVanished()).count() +
|
||||
redisPlayers.values().stream().filter(all -> !all.isVanished()).count());
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.STAFF_ONLINE, 1000, () ->
|
||||
Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(all -> !all.isVanished() && all.hasPermission(TabConstants.Permission.STAFF)).count() +
|
||||
redisPlayers.values().stream().filter(all -> !all.isVanished() && all.isStaff()).count());
|
||||
TabAPI.getInstance().getEventBus().register(TabPlaceholderRegisterEvent.class, eventHandler);
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) onJoin(p);
|
||||
sendMessage("{\"action\":\"loadrequest\",\"proxy\":\"" + proxy.toString() + "\"}");
|
||||
}
|
||||
@@ -341,6 +347,7 @@ public abstract class RedisSupport extends TabFeature {
|
||||
public void unload() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) onQuit(p);
|
||||
unregister();
|
||||
TabAPI.getInstance().getEventBus().unregister(eventHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -421,7 +428,7 @@ public abstract class RedisSupport extends TabFeature {
|
||||
public void onLoginPacket(TabPlayer packetReceiver) {
|
||||
for (RedisPlayer p : redisPlayers.values()) {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
all.sendCustomPacket(p.getRegisterTeamPacket());
|
||||
all.sendCustomPacket(p.getRegisterTeamPacket(), TabConstants.PacketCategory.NAMETAGS_TEAM_REGISTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
package me.neznamy.tab.shared.features.scoreboard;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore.Action;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.api.scoreboard.Line;
|
||||
import me.neznamy.tab.api.scoreboard.Scoreboard;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.scoreboard.lines.*;
|
||||
import me.neznamy.tab.shared.placeholders.conditions.Condition;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class representing a scoreboard configured in config
|
||||
*/
|
||||
public class ScoreboardImpl extends TabFeature implements Scoreboard {
|
||||
|
||||
@Getter private final String featureName = "Scoreboard";
|
||||
@Getter private final String refreshDisplayName = "Updating Scoreboard title";
|
||||
|
||||
//scoreboard manager
|
||||
@Getter private final ScoreboardManagerImpl manager;
|
||||
|
||||
@@ -79,7 +81,6 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
|
||||
* lines of scoreboard
|
||||
*/
|
||||
public ScoreboardImpl(ScoreboardManagerImpl manager, String name, String title, List<String> lines, boolean dynamicLinesOnly) {
|
||||
super(manager.getFeatureName(), "Updating scoreboard title");
|
||||
this.manager = manager;
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
@@ -143,8 +144,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
|
||||
}
|
||||
manager.getActiveScoreboards().put(p, this);
|
||||
recalculateScores(p);
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setScoreboardName(p, name);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardName(p, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,8 +163,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
|
||||
}
|
||||
players.remove(p);
|
||||
manager.getActiveScoreboards().remove(p);
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setScoreboardName(p, "");
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardName(p, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-35
@@ -1,19 +1,17 @@
|
||||
package me.neznamy.tab.shared.features.scoreboard;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
import me.neznamy.tab.api.scoreboard.Scoreboard;
|
||||
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Feature handler for scoreboard feature
|
||||
@@ -24,7 +22,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
public static final int DISPLAY_SLOT = 1;
|
||||
|
||||
//config options
|
||||
private final String toggleCommand = TAB.getInstance().getConfiguration().getConfig().getString("scoreboard.toggle-command", "/sb");
|
||||
@Getter private final String toggleCommand = TAB.getInstance().getConfiguration().getConfig().getString("scoreboard.toggle-command", "/sb");
|
||||
@Getter private final boolean usingNumbers = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard.use-numbers", false);
|
||||
private final boolean rememberToggleChoice = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard.remember-toggle-choice", false);
|
||||
private final boolean hiddenByDefault = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard.hidden-by-default", false);
|
||||
@@ -36,12 +34,9 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
@Getter private final Map<String, Scoreboard> registeredScoreboards = new LinkedHashMap<>();
|
||||
private Scoreboard[] definedScoreboards;
|
||||
|
||||
//toggle messages
|
||||
private final String scoreboardOn = TAB.getInstance().getConfiguration().getMessages().getScoreboardOn();
|
||||
private final String scoreboardOff = TAB.getInstance().getConfiguration().getMessages().getScoreboardOff();
|
||||
|
||||
//list of players with disabled scoreboard
|
||||
private final List<String> sbOffPlayers;
|
||||
private final List<String> sbOffPlayers = rememberToggleChoice ? TAB.getInstance().getConfiguration().getPlayerDataFile()
|
||||
.getStringList("scoreboard-off", new ArrayList<>()) : Collections.emptyList();
|
||||
|
||||
//active scoreboard announcement
|
||||
private Scoreboard announcement;
|
||||
@@ -51,18 +46,19 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
@Getter private final WeakHashMap<TabPlayer, ScoreboardImpl> activeScoreboards = new WeakHashMap<>();
|
||||
private final Set<TabPlayer> visiblePlayers = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
@Getter private final WeakHashMap<TabPlayer, String> otherPluginScoreboards = new WeakHashMap<>();
|
||||
@Getter private final String featureName = "Scoreboard";
|
||||
@Getter private final String refreshDisplayName = "Switching scoreboards";
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads configuration
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ScoreboardManagerImpl() {
|
||||
super("Scoreboard", "Switching displayed scoreboard", "scoreboard");
|
||||
if (rememberToggleChoice) {
|
||||
sbOffPlayers = Collections.synchronizedList(new ArrayList<>(TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("scoreboard-off", new ArrayList<>())));
|
||||
} else {
|
||||
sbOffPlayers = Collections.emptyList();
|
||||
}
|
||||
super("scoreboard");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void load() {
|
||||
Map<String, Map<String, Object>> map = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("scoreboard.scoreboards");
|
||||
for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
|
||||
String condition = (String) entry.getValue().get("display-condition");
|
||||
@@ -81,18 +77,6 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.scoreboardLine(entry.getKey()), sb);
|
||||
}
|
||||
definedScoreboards = registeredScoreboards.values().toArray(new Scoreboard[0]);
|
||||
if (respectOtherPlugins) {
|
||||
PipelineInjector inj = (PipelineInjector) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PIPELINE_INJECTION);
|
||||
//null check if injection was disabled in config or velocity
|
||||
if (inj != null) inj.setByteBufDeserialization(true);
|
||||
}
|
||||
TAB.getInstance().debug(String.format("Loaded Scoreboard feature with parameters toggleCommand=%s, useNumbers=%s, disabledWorlds=%s"
|
||||
+ ", disabledServers=%s, rememberToggleChoice=%s, hiddenByDefault=%s, scoreboard_on=%s, scoreboard_off=%s, staticNumber=%s, joinDelay=%s",
|
||||
toggleCommand, usingNumbers, Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), rememberToggleChoice, hiddenByDefault, scoreboardOn, scoreboardOff, staticNumber, joinDelay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
onJoin(p);
|
||||
}
|
||||
@@ -286,7 +270,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
visiblePlayers.add(player);
|
||||
sendHighestScoreboard(player);
|
||||
if (sendToggleMessage) {
|
||||
player.sendMessage(scoreboardOn, true);
|
||||
player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getScoreboardOn(), true);
|
||||
}
|
||||
if (rememberToggleChoice) {
|
||||
if (hiddenByDefault) {
|
||||
@@ -302,7 +286,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
visiblePlayers.remove(player);
|
||||
unregisterScoreboard(player);
|
||||
if (sendToggleMessage) {
|
||||
player.sendMessage(scoreboardOff, true);
|
||||
player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getScoreboardOff(), true);
|
||||
}
|
||||
if (rememberToggleChoice) {
|
||||
if (hiddenByDefault) {
|
||||
@@ -315,8 +299,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
}
|
||||
}
|
||||
}
|
||||
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
|
||||
if (expansion != null) expansion.setScoreboardVisible(player, visible);
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardVisible(player, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-1
@@ -19,6 +19,9 @@ import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
|
||||
*/
|
||||
public abstract class ScoreboardLine extends TabFeature implements Line {
|
||||
|
||||
@Getter private final String featureName = "Scoreboard";
|
||||
@Getter private final String refreshDisplayName = "Updating Scoreboard lines";
|
||||
|
||||
//ID of this line
|
||||
protected final int lineNumber;
|
||||
|
||||
@@ -43,7 +46,6 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
|
||||
* ID of this line
|
||||
*/
|
||||
protected ScoreboardLine(ScoreboardImpl parent, int lineNumber) {
|
||||
super(parent.getFeatureName(), "Updating scoreboard lines");
|
||||
this.parent = parent;
|
||||
this.lineNumber = lineNumber;
|
||||
teamName = "TAB-SB-TM-" + lineNumber;
|
||||
|
||||
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user