Compare commits

...
66 Commits
Author SHA1 Message Date
NEZNAMY e1c152d95d Add missing change in previous commit 2022-06-11 18:00:08 +02:00
NEZNAMY dffac5deb8 BungeeCord - Retry plugin message processing if player isn't loaded for any reason (#564) 2022-06-11 17:57:18 +02:00
NEZNAMY 8aa3bbfad3 Scoreboard - Fix RGB codes not translating into legacy correctly in long lines 2022-06-09 23:10:44 +02:00
NEZNAMY 5bb4006e55 Scoreboard - Fix additional empty lines when use-numbers is enabled 2022-06-08 00:46:32 +02:00
NEZNAMY e06a406682 Clean up repositories 2022-06-08 00:45:21 +02:00
NEZNAMY a19719be4b Configuration - Remove deleted placeholder from default config 2022-06-03 23:47:41 +02:00
NEZNAMY 35856a9f3d Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-06-02 15:50:46 +02:00
NEZNAMY cf1d5d5ee2 Code style - Remove unused method 2022-06-01 20:23:16 +02:00
NEZNAMY f69a845e51 Bukkit - Implement chat message sending on <1.7 2022-05-31 12:21:07 +02:00
NEZNAMY db67e93bb8 Disable compilation of a module 2022-05-31 11:51:21 +02:00
NEZNAMY 313f089222 Aligned Suffix - Fix memory leak 2022-05-20 01:10:00 +02:00
NEZNAMY 458c08ded7 Tab expansion - Slightly optimize placeholder retrieving 2022-05-18 01:39:20 +02:00
NEZNAMY fe6cc7417f Placeholders - Fix refresh interval forwarding to bridge (#597) (2nd attempt) 2022-05-17 19:00:23 +02:00
NEZNAMY 181c94a808 Code style - Extract strings into constants 2022-05-13 00:42:30 +02:00
NEZNAMY 9ff6f02d8d Code style - Remove unused code and other minor changes 2022-05-12 22:49:41 +02:00
NEZNAMY 3cf74516d9 Bukkit - Remove unused data watcher registries 2022-05-12 22:25:18 +02:00
NEZNAMY 31f5502c16 BungeeCord - Delete TAB's bungeecord events (to make users switch to universal ones) 2022-05-12 22:04:37 +02:00
NEZNAMY 671d60bd72 API - Throw user-friendly exception if player is not loaded yet (#585) 2022-05-10 18:32:09 +02:00
NEZNAMY 294e866501 Placeholders - Fix refresh interval forwarding to bridge (#597) 2022-05-10 18:21:13 +02:00
NEZNAMY 96789100e3 Bukkit - Wrap incoming components instead of deserializing and then serializing them 2022-05-10 13:22:27 +02:00
NEZNAMY ea934fc7fb GroupManager - Add null check for player group from permission plugin 2022-05-08 12:48:06 +02:00
NEZNAMY d529b7b340 Sorting - Allow placeholders with ":" inside to work properly in PLACEHOLDER sorting type 2022-05-08 12:47:40 +02:00
NEZNAMY 092ca7f9f8 Aligned Suffix - Assume players can see themselves when vanished 2022-05-03 18:03:29 +02:00
NEZNAMY 79f4bdb869 Unlimited Nametags - Do not delay respawn by 1 tick anymore to fix OOM 2022-05-01 13:07:42 +02:00
NEZNAMY b0d2d0f7dc NickCompatibility - Clean up code 2022-04-30 20:01:40 +02:00
NEZNAMY cdf58c1dfe Teams - set canSeeFriendlyInvisibles to true 2022-04-30 18:21:46 +02:00
NEZNAMY 4680de6022 Feature management - Add try/catch for each feature in join processing (#560) 2022-04-30 17:22:03 +02:00
NEZNAMY e8f61181b6 Error logging - Log (critical) errors into console even when error file is full 2022-04-30 17:20:24 +02:00
NEZNAMY 75e9e007d0 Fix disabled worlds not loading correctly (from #6f07cf4) 2022-04-28 17:27:23 +02:00
NEZNAMY 3574ef7c0b Belowname - Do not update score when entering disabled world 2022-04-28 12:20:54 +02:00
NEZNAMY 86158036d2 Commands - Make permission group assigning more obvious in debug output 2022-04-28 11:55:21 +02:00
NEZNAMY e73beb47a3 API - Fix scoreboard Line#setText not working 2022-04-26 23:23:42 +02:00
NEZNAMY 0db5f1dd73 Scoreboard - Fix NPE when a line is null 2022-04-23 14:18:30 +02:00
NEZNAMY 6dcdc2aa58 PetFix - Fix attack packet cancellation 2022-04-20 10:46:08 +02:00
NEZNAMY 64601137e8 Scoreboard - Show empty lines when defined as empty, only hide when placeholder output is empty 2022-04-15 00:07:44 +02:00
NEZNAMY af7697b670 BungeeCord - Fixed NPE from moving to reflection 2022-04-14 23:24:45 +02:00
NEZNAMY 9e4f46088b Commands - Do not force refresh players who are not loaded yet (#560) 2022-04-12 11:21:06 +02:00
NEZNAMY 91e71a9b9c Layout - Fixed NPE when player had no layout visible 2022-04-10 23:32:13 +02:00
NEZNAMY 1198ffb847 BungeeCord - Use reflection for proxy module access due to the repository no longer being available 2022-04-10 23:31:51 +02:00
NEZNAMY 72116ecd0c Javadoc - Further improve documentation 2022-04-10 21:08:19 +02:00
NEZNAMY 04ebb7123d Code style - Move NMS object declaration and initialization 2022-04-10 20:28:29 +02:00
NEZNAMY 43096beafe Bukkit - Use ConsoleCommandSender for colored messages (#555) 2022-04-09 18:27:37 +02:00
NEZNAMY 2a08c62456 Tab expansion - Fix server placeholders not loading on join 2022-04-07 15:44:58 +02:00
NEZNAMY b62d74c3fa Code style - Remove unnecessary reflection 2022-04-06 20:38:03 +02:00
NEZNAMY 652fb5a6ad Bukkit - Switch from Paper to Purpur API 2022-04-06 19:11:19 +02:00
NEZNAMY d1f25cc19f Bukkit - Disable checking for plugins with compatibility issue 2022-04-06 18:25:12 +02:00
NEZNAMY dc6a9d9761 Fix layout startup 2022-04-06 13:44:16 +02:00
NEZNAMY 838d725715 Code style - Removed redundant static modifiers 2022-04-06 13:31:31 +02:00
NEZNAMY c45acb39ca Code style - Clean up variable initialization 2022-04-06 13:10:11 +02:00
NEZNAMY 957edf2a52 Javadoc - Fixed compilation warn 2022-04-06 12:44:54 +02:00
NEZNAMY d969c6ed0e Code style - Change way of checking if server is proxy 2022-04-06 12:41:20 +02:00
NEZNAMY dd6fca961c Code style - Clean up configuration loading 2022-04-06 12:34:39 +02:00
NEZNAMY e88a6df456 Config - remove hidden unregister-before-register option and remove the function entirely. Hopefully with improved checks this should not be necessary anymore. It caused client errors, as well as #549 2022-04-06 11:35:03 +02:00
NEZNAMY 3a4530c999 RGB - Finish #552 - Only replace clean gradients in property initialization 2022-04-05 23:24:37 +02:00
NEZNAMY b623dba40a Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-04-05 23:20:13 +02:00
NEZNAMY e2fd945109 Merge pull request #552 from lucypoulton/feat/kyori-rgb-format
Add support for Kyori MiniMessage RGB format
2022-04-05 23:19:52 +02:00
NEZNAMY e6dc3cda31 Unlimited nametags - Manually destroy armor stands on world switch 2022-04-05 20:21:36 +02:00
Lucy Poulton 18d8ff127a add support for kyori minimessage rgb format 2022-04-05 14:25:22 +01:00
NEZNAMY 4a9526451f Code style - Remove redundant "triggerMode" flag from Placeholder class due being redundant. Setting refresh to -1 has the same effect. 2022-04-04 19:03:51 +02:00
NEZNAMY 669d89c870 Code style - Move tab expansion code to interface 2022-04-04 18:15:06 +02:00
NEZNAMY 39161f2af5 Nametags - Reduced CPU usage of anti-override 2022-04-04 16:02:32 +02:00
NEZNAMY be8c33e3fc Velocity - Delay join event processing to avoid unformatted tablist names 2022-04-03 14:08:19 +02:00
NEZNAMY 7c1a27f9df Javadoc - Further improve documentation 2022-04-02 19:07:18 +02:00
NEZNAMY 342cc93dbf Code style - Clean up code 2022-04-02 13:17:15 +02:00
NEZNAMY 6f07cf439b Code style - Clean up loading of disabled servers/worlds 2022-04-01 23:40:04 +02:00
NEZNAMY 1d4d00b69b Code style - Clean up code with usage of @Inject 2022-04-01 23:21:30 +02:00
116 changed files with 1743 additions and 1336 deletions

No files matched your search

@@ -1,5 +1,8 @@
package me.neznamy.tab.api;
/**
* Feature manager allows registration and work with features.
*/
public interface FeatureManager {
/**
@@ -1,5 +1,9 @@
package me.neznamy.tab.api;
/**
* An interface representing a property assigned to a player,
* which may contain placeholders and can be changed using the API.
*/
public interface Property {
/**
@@ -3,30 +3,124 @@ package me.neznamy.tab.api;
import java.util.*;
import java.util.stream.Collectors;
/**
* Interface representing user or group configuration.
* Implementation is either using file or MySQL, if it's enabled.
*/
public interface PropertyConfiguration {
void setProperty(String name, String property, String server, String world, String value);
/**
* Sets property value of group or user to specified value. If {@code world} or
* {@code server} is not {@code null}, value will be inserted as a per-world or
* per-server setting. If both {@code world} and {@code server} are {@code null},
* value is inserted as a global setting.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @param property
* Name of property to set
* @param server
* Name of server to apply setting to, {@code null} for global setting
* @param world
* Name of world to apply setting to, {@code null} for global setting
* @param value
* Value of the property
*/
void setProperty(String groupOrUser, String property, String server, String world, String value);
String[] getProperty(String name, String property, String server, String world);
/**
* Gets property of group or user. If {@code server} or {@code world}
* are not {@code null}, per-server / per-world settings are checked first
* to try to find a match. If no match is found, global value is returned.
* Returned value is an array with 2 elements, with first argument being value
* and second being source. If nothing is found, empty array is returned.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @param property
* Name of property to get
* @param server
* Server the player is currently in, to allow check for per-server settings
* @param world
* World the player is currently in, to allow check for per-world settings
* @return Array with 2 elements with value being first, source second if found,
* empty array if nothing was found.
*/
String[] getProperty(String groupOrUser, String property, String server, String world);
void remove(String name);
/**
* Removes all data applied to specified group or user.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
*/
void remove(String groupOrUser);
Map<String, Object> getGlobalSettings(String name);
/**
* Returns map of global settings applied to specified group or user.
* Map key is property name, value is property value.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of global settings of specified group or user
*/
Map<String, Object> getGlobalSettings(String groupOrUser);
Map<String, Map<String, Object>> getPerWorldSettings(String name);
/**
* Returns map of per-world settings of specified group or user.
* Map key is world name, value is property name-value map in that world.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of per-world settings of specified group or user
*/
Map<String, Map<String, Object>> getPerWorldSettings(String groupOrUser);
Map<String, Map<String, Object>> getPerServerSettings(String name);
/**
* Returns map of per-server settings of specified group or user.
* Map key is server name, value is property name-value map in that server.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of per-server settings of specified group or user
*/
Map<String, Map<String, Object>> getPerServerSettings(String groupOrUser);
/**
* Returns set of all groups or users that have anything configured,
* either globally or in some world or server.
*
* @return Set of all entries with something configured
*/
Set<String> getAllEntries();
default Map<String, Map<String, Object>> convertMap(Map<String, Map<String, Map<String, Object>>> map, String key) {
/**
* Converts per-world or per-server map into a per-world or per-server map
* with values only belonging to entered group or user. In other words, extracts
* settings belonging to group or user from the map.
*
* @param map
* Original per-world or per-server setting map
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Converted map only containing data of specified group or user
*/
default Map<String, Map<String, Object>> convertMap(Map<String, Map<String, Map<String, Object>>> map, String groupOrUser) {
Map<String, Map<String, Object>> converted = new HashMap<>();
for (Map.Entry<String, Map<String, Map<String, Object>>> entry : map.entrySet()) {
converted.put(entry.getKey(), entry.getValue().get(key));
converted.put(entry.getKey(), entry.getValue().get(groupOrUser));
}
return converted;
}
/**
* Converts object into string. For most objects, {@link Object#toString()} is called.
* For lists, entries are connected with {@code "\n"}.
*
* @param obj
* Object to convert
* @return Converted string
*/
@SuppressWarnings("unchecked")
default String toString(Object obj) {
if (obj instanceof List) {
@@ -35,6 +129,15 @@ public interface PropertyConfiguration {
return obj.toString();
}
/**
* Converts string into object. If string contains {@code "\n"},
* a list is created using {@code "\n"} as a separator. Otherwise,
* inserted string is returned.
*
* @param string
* String to convert
* @return Converted object
*/
default Object fromString(String string) {
if (string != null && string.contains("\n")) {
return Arrays.asList(string.split("\n"));
@@ -65,13 +65,13 @@ public enum ProtocolVersion {
/** Value array to iterate over to avoid array creations on each call */
public static final ProtocolVersion[] VALUES = values();
//version's network id found at https://wiki.vg/Protocol_version_numbers
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
private final int networkId;
//minor version, such as 16
/** Version's minor version, such as 16 for 1.16.x */
private int minorVersion;
//friendly name displayed in %player-version% placeholder
/** Version's friendly name displayed in %player-version% placeholder */
private final String friendlyName;
/**
@@ -15,14 +15,31 @@ import me.neznamy.tab.api.team.TeamManager;
*/
public abstract class TabAPI {
/** Instance of the API */
private static TabAPI instance;
/**
* Returns API instance. If instance was not set by the plugin, throws
* {@code IllegalStateException}. This is usually caused by shading the API
* into own project, which is not allowed. Another option is calling the method
* before plugin was able to load.
*
* @return API instance
* @throws IllegalStateException
* If instance is {@code null}
*/
public static TabAPI getInstance() {
if (instance == null) throw new IllegalStateException("API instance is null. This likely means you shaded TAB's API into your project" +
" instead of only using it, which is not allowed.");
return instance;
}
/**
* Instance setter for internal use by the plugin only.
*
* @param instance
* API instance
*/
public static void setInstance(TabAPI instance) {
TabAPI.instance = instance;
}
@@ -53,26 +70,31 @@ public abstract class TabAPI {
public abstract TabPlayer[] getOnlinePlayers();
/**
* Return BossBar manager instance if the feature is enabled. Returns null otherwise.
* Return BossBar manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return BossBar manager
*/
public abstract BossBarManager getBossBarManager();
/**
* Returns scoreboard manager instance if the feature is enabled. Returns null otherwise.
* Returns scoreboard manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return scoreboard manager
*/
public abstract ScoreboardManager getScoreboardManager();
/**
* Returns team manager instance if the feature is enabled, false otherwise
* Returns team manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return team manager
*/
public abstract TeamManager getTeamManager();
/**
* Returns header/footer manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Header/footer manager
*/
public abstract HeaderFooterManager getHeaderFooterManager();
/**
@@ -89,6 +111,11 @@ public abstract class TabAPI {
*/
public abstract FeatureManager getFeatureManager();
/**
* Returns Tablist name format manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Tablist name format manager
*/
public abstract TablistFormatManager getTablistFormatManager();
/**
@@ -111,23 +138,70 @@ public abstract class TabAPI {
* @param message
* message to print
* @param translateColors
* true if colors should be translated, false if not
* {@code true} if colors should be translated, {@code false} if not
*/
public abstract void sendConsoleMessage(String message, boolean translateColors);
/**
* Returns TAB's Thread manager, which allows task submitting
*
* @return ThreadManager instance
*/
public abstract ThreadManager getThreadManager();
/**
* Returns TAB's cache file used to store player toggle data
*
* @return TAB's player cache file
*/
public abstract ConfigurationFile getPlayerCache();
/**
* Returns TAB's config.yml file
*
* @return config.yml file
*/
public abstract ConfigurationFile getConfig();
/**
* Returns TAB's group configuration
*
* @return TAB's group configuration
*/
public abstract PropertyConfiguration getGroups();
/**
* Returns TAB's user configuration
*
* @return TAB's user configuration
*/
public abstract PropertyConfiguration getUsers();
/**
* Sends a debug message into console if the option
* is enabled in config.
*
* @param message
* Message to send
*/
public abstract void debug(String message);
/**
* Logs an error into errors.log file
*
* @param message
* Error message
* @param t
* Thrown error
*/
public abstract void logError(String message, Throwable t);
/**
* Sets name of file with syntax error, which prevented
* the plugin from enabling. Internal use only.
*
* @param file
* Name of file with syntax error
*/
public abstract void setBrokenFile(String file);
}
@@ -6,38 +6,53 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
/**
* Abstract class representing a core feature of the plugin.
* <p>
* It receives all kinds of events and can react to them.
*/
public abstract class TabFeature {
/** Feature's name displayed in /tab cpu */
private final String featureName;
/** Feature's function name displayed in place of refreshing in /tab cpu */
private final String refreshDisplayName;
protected final String[] disabledServers;
private final boolean serverWhitelistMode;
protected final String[] disabledWorlds;
private final boolean worldWhitelistMode;
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledServers = new String[0];
/** Flag tracking whether disabled server list is a whitelist or blacklist */
private boolean serverWhitelistMode = false;
/** Worlds where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledWorlds = new String[0];
/** Flag tracking whether disabled world list is a whitelist or blacklist */
private boolean worldWhitelistMode = false;
/** Players located in currently disabled worlds / servers */
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
/**
* List of methods overridden (implemented) by each feature.
* If a method is not overridden, it is not called at all.
* This avoids a massive spam for every single method
* in every single feature in /tab cpu output
*/
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
*/
protected TabFeature(String featureName, String refreshDisplayName) {
this(featureName, refreshDisplayName, null, null);
}
protected TabFeature(String featureName, String refreshDisplayName, List<String> disabledServers, List<String> disabledWorlds) {
this.featureName = featureName;
this.refreshDisplayName = refreshDisplayName;
if (disabledServers != null) {
this.disabledServers = disabledServers.toArray(new String[0]);
serverWhitelistMode = disabledServers.contains("WHITELIST");
} else {
this.disabledServers = new String[0];
serverWhitelistMode = false;
}
if (disabledWorlds != null) {
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
} else {
this.disabledWorlds = new String[0];
worldWhitelistMode = false;
}
try {
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onCommand");
@@ -71,7 +86,34 @@ public abstract class TabFeature {
}
/**
* Loads all players and sends packets
* Constructs new instance with given parameters and loads method overrides.
* 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);
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) {
this.disabledServers = disabledServers.toArray(new String[0]);
serverWhitelistMode = disabledServers.contains("WHITELIST");
}
if (disabledWorlds != null) {
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
}
}
/**
* Loads all online players and sends packets
*/
public void load() {
//empty by default
@@ -85,13 +127,13 @@ public abstract class TabFeature {
}
/**
* Processes command from player
* Processes command from player. This is typically a toggle command.
*
* @param sender
* command sender
* @param message
* command line
* @return true if event should be cancelled, false if not
* @return {@code true} if event should be cancelled, {@code true} if not
*/
public boolean onCommand(TabPlayer sender, String message) {
return false;
@@ -232,6 +274,12 @@ public abstract class TabFeature {
//empty by default
}
/**
* Processes vanish status change of player
*
* @param player
* Player who changed vanish status
*/
public void onVanishStatusChange(TabPlayer player){
//empty by default
}
@@ -248,13 +296,13 @@ public abstract class TabFeature {
}
/**
* Returns true if world or server is disabled, false if not
* Returns {@code true} if world or server is disabled, {@code false} if not
*
* @param server
* server to check
* @param world
* world to check
* @return true if feature should be disabled, false if not
* @return {@code true} if feature should be disabled, {@code false} if not
*/
public boolean isDisabled(String server, String world) {
boolean contains = contains(disabledWorlds, world);
@@ -265,6 +313,17 @@ public abstract class TabFeature {
return contains;
}
/**
* Returns {@code true} if list contains the specified element or element
* ends with {@code "*"} and element meeting that requirement is present,
* {@code false} otherwise.
*
* @param list
* List to check
* @param element
* Element to find
* @return {@code true} if element was found, {@code false} if not
*/
protected boolean contains(String[] list, String element) {
if (element == null) return false;
for (String s : list) {
@@ -286,24 +345,60 @@ public abstract class TabFeature {
return featureName;
}
/**
* Returns text to display in /tab cpu of this feature refreshing visuals
*
* @return Text to display as refreshing of this feature
*/
public String getRefreshDisplayName() {
return refreshDisplayName;
}
/**
* Returns {@code true} if method with specified name is overridden
* in the implementation, {@code false} if not
*
* @param method
* Method to check
* @return {@code true} if overridden, {@code false} if not
*/
public boolean overridesMethod(String method) {
return methodOverrides.contains(method);
}
/**
* Returns {@code true} if player is currently located in a server
* or world, which is marked as disabled, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if player is in disabled server / world,
* {@code false} if not
*/
public boolean isDisabledPlayer(TabPlayer p) {
return disabledPlayers.contains(p);
}
/**
* Adds specified player into list of players in disabled
* servers / worlds.
*
* @param p
* Player to add
*/
public void addDisabledPlayer(TabPlayer p) {
if (disabledPlayers.contains(p)) return;
disabledPlayers.add(p);
}
/**
* Removes specified player from list of players in disabled
* servers / worlds
*
* @param p
* Player to check
*/
public void removeDisabledPlayer(TabPlayer p) {
disabledPlayers.remove(p);
}
public String getRefreshDisplayName() {
return refreshDisplayName;
}
}
@@ -114,34 +114,12 @@ public interface TabPlayer {
*/
void sendPacket(Object packet);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
* is displayed in /tab cpu
*
* @param packet
* an instance of packet depending on platform
* @param feature
* feature to increment sent packet counter of
*/
void sendPacket(Object packet, TabFeature feature);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
* is displayed in /tab cpu
*
* @param packet
* an instance of packet depending on platform
* @param feature
* feature to increment sent packet counter of
*/
void sendPacket(Object packet, String feature);
/**
* Returns player's property by name
*
* @param name
* name of property
* @return the property or null if not found
* @return the property or {@code null} if not found
*/
Property getProperty(String name);
@@ -164,14 +142,7 @@ public interface TabPlayer {
void sendMessage(IChatBaseComponent message);
/**
* Returns player's primary permission group
*
* @return player's group depending on configuration
*/
String getGroup();
/**
* Returns player's channel or null if server is 1.7 or older
* Returns player's netty channel. On servers running version 1.7 or older returns {@code null}.
*
* @return player's channel
*/
@@ -194,13 +165,14 @@ public interface TabPlayer {
/**
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
*
* @return true if player is fully loaded, false otherwise
* @return {@code true} if player is fully loaded, {@code false} otherwise
*/
boolean isLoaded();
/**
* Sets property with specified name to new value. If property did not exist before, it is
* created and true is returned. If it existed, it is overridden and true is returned. False is returned otherwise.
* created and {@code true} is returned. If it existed, it is overridden and {@code true} is returned.
* {@code false} is returned otherwise.
*
* @param feature
* feature using this property to get placeholders registered
@@ -208,7 +180,7 @@ public interface TabPlayer {
* property name
* @param rawValue
* new raw value
* @return true if value changed / did not exist, false if value did not change
* @return {@code true} if value changed / did not exist, {@code false} if value did not change
*/
boolean setProperty(TabFeature feature, String identifier, String rawValue);
@@ -217,24 +189,24 @@ public interface TabPlayer {
*
* @param property
* property name to load
* @return true if value did not exist or changed, false otherwise
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property);
/**
* Loads property from config using standard property loading algorithm. If the property is
* not set in config, sets it to ifNotSet value
* not set in config, {@code ifNotSet} value is used.
*
* @param property
* property name to load
* @param ifNotSet
* value to use if property is not defined in config
* @return true if value did not exist or changed, false otherwise
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
/**
* Returns name of player's scoreboard team or null if NameTag feature is disabled
* Returns name of player's scoreboard team or {@code null} if NameTag feature is disabled
*
* @return name of player's team
*/
@@ -248,32 +220,31 @@ public interface TabPlayer {
String getTeamNameNote();
/**
* Returns true if player is disguised using LibsDisguises
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
*
* @return true if player is disguised, false if not
* @return {@code true} if player is disguised, {@code false} if not
*/
boolean isDisguised();
/**
* Returns true if player has invisibility potion, false if not. For bukkit, API is used, for BungeeCord
* bukkit bridge is used
* Returns {@code true} if player has invisibility potion, {@code false} if not.
* For bukkit, API is used, for BungeeCord, bridge is used.
*
* @return true if player has invisibility potion, false if not
* @return {@code true} if player has invisibility potion, {@code false} if not
*/
boolean hasInvisibilityPotion();
/**
* Currently only PV hook on BungeeCord for global PlayerList function. Looking to expand this
* in the future
* Checks if player is vanished using any supported vanish plugin and returns the result.
*
* @return true if vanished with PV on BungeeCord
* @return {@code true} if player is vanished, {@code false} if not
*/
boolean isVanished();
/**
* Returns true if player is online according to server
* Calls platform-specific method and returns the result
*
* @return true if online, false if not
* @return {@code true} if player is online, {@code false} if not
*/
boolean isOnline();
@@ -283,14 +254,60 @@ public interface TabPlayer {
* @return GameMode of the player
*/
int getGamemode();
/**
* Returns {@code true} if this is a bedrock player, {@code false} if not
*
* @return {@code true} if player is a bedrock player, {@code false} if not
*/
boolean isBedrockPlayer();
/**
* Returns player's primary permission group. If group has been changed using
* {@link #setTemporaryGroup(String)}, returns that value. Otherwise, returns group
* detected by standard group assign logic
*
* @return player's primary permission group
*/
String getGroup();
/**
* Temporarily overrides player's group and applies all changes coming from new group.
* This includes all properties and sorting, if used.
*
* @param group
* New group to use
* @see #hasTemporaryGroup()
* @see #resetTemporaryGroup()
*/
void setTemporaryGroup(String group);
/**
* Returns temporary group applied to the player using {@link #setTemporaryGroup(String)}.
* If no group was set, returns {@code null}
*
* @return Temporary group assigned to player or {@code null} if not set
* @see #setTemporaryGroup(String)
* @see #resetTemporaryGroup()
*/
boolean hasTemporaryGroup();
/**
* Resets temporary group assigned using {@link #setTemporaryGroup(String)}.
* If no temporary group is set, doesn't do anything.
*
* @see #setTemporaryGroup(String)
* @see #hasTemporaryGroup()
*/
void resetTemporaryGroup();
/**
* Returns player's game profile name as seen by other players. By default,
* this returns player's name. If using a nickname / disguise plugin that changes
* game profile name for other players and change was successfully detected, returns
* that value.
*
* @return player's game profile name as seen by other players
*/
String getNickname();
}
@@ -1,29 +1,168 @@
package me.neznamy.tab.api;
/**
* An interface allowing to modify tablist display names
* of players.
* <p>
* Instance can be obtained using {@link TabAPI#getTablistFormatManager()}.
* This requires the feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
public interface TablistFormatManager {
/**
* Changes player's prefix to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param prefix
* new prefix value
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void setPrefix(TabPlayer player, String prefix);
/**
* Changes player's name to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param customName
* new customName value
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void setName(TabPlayer player, String customName);
/**
* Changes player's suffix to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param suffix
* new suffix value
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void setSuffix(TabPlayer player, String suffix);
/**
* Resets player's prefix previously assigned using {@link #setPrefix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset prefix of
* @see #setPrefix(TabPlayer, String)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void resetPrefix(TabPlayer player);
/**
* Resets player's custom name previously assigned using {@link #setName(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset name of
* @see #setName(TabPlayer, String)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void resetName(TabPlayer player);
/**
* Resets player's suffix previously assigned using {@link #setSuffix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset suffix of
* @see #setSuffix(TabPlayer, String)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void resetSuffix(TabPlayer player);
/**
* Returns prefix assigned using {@link #setPrefix(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom prefix of
* @return player's custom prefix or {@code null} if not defined
* @see #setPrefix(TabPlayer, String)
* @see #resetPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
String getCustomPrefix(TabPlayer player);
/**
* Returns custom name assigned using {@link #setName(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom name of
* @return player's custom name or {@code null} if not defined
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
String getCustomName(TabPlayer player);
/**
* Returns suffix assigned using {@link #setSuffix(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom suffix of
* @return player's custom suffix or {@code null} if not defined
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
String getCustomSuffix(TabPlayer player);
/**
* Returns player's original prefix applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original prefix of
* @return player's original prefix
* @see #setPrefix(TabPlayer, String)
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
*/
String getOriginalPrefix(TabPlayer player);
/**
* Returns player's original name applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original name of
* @return player's original name
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
*/
String getOriginalName(TabPlayer player);
/**
* Returns player's original suffix applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original suffix of
* @return player's original suffix
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
*/
String getOriginalSuffix(TabPlayer player);
}
@@ -4,15 +4,16 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
/**
* An interface allowing work with BossBars such as creating, sending
* and toggling.
* <p>
* Instance can be obtained using {@link me.neznamy.tab.api.TabAPI#getBossBarManager()}.
* Instance can be obtained using {@link TabAPI#getBossBarManager()}.
* This requires the BossBar feature to be enabled in config, otherwise the method will
* return null.
* return {@code null}.
*/
public interface BossBarManager {
@@ -231,7 +231,7 @@ public class IChatBaseComponent {
*/
public static IChatBaseComponent fromColoredText(String originalText){
Preconditions.checkNotNull(originalText, "text");
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText), false);
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
List<IChatBaseComponent> components = new ArrayList<>();
StringBuilder builder = new StringBuilder();
IChatBaseComponent component = new IChatBaseComponent();
@@ -0,0 +1,75 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.ProtocolVersion;
import java.util.List;
/**
* A class extending IChatBaseComponent that holds the original value
* to avoid deserializing it, which is an unnecessary operation that
* slows down the performance, as well as makes code more complicated
* and open to problems.
*/
public class WrappedChatComponent extends IChatBaseComponent {
/** Original NMS component */
private final Object originalComponent;
/**
* Constructs new instance with given parameter
*
* @param originalComponent
* Original NMS component
*/
public WrappedChatComponent(Object originalComponent) {
this.originalComponent = originalComponent;
}
/**
* Returns the original NMS component
* @return the original NMS component
*/
public Object get() {
return originalComponent;
}
@Override
public String toString() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String toString(ProtocolVersion clientVersion) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public List<IChatBaseComponent> getExtra(){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String getText() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public ChatModifier getModifier() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void addExtra(IChatBaseComponent child) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void setModifier(ChatModifier modifier) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
}
@@ -8,6 +8,7 @@ import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.chat.rgb.format.BukkitFormat;
import me.neznamy.tab.api.chat.rgb.format.CMIFormat;
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat;
import me.neznamy.tab.api.chat.rgb.format.KyoriFormat;
import me.neznamy.tab.api.chat.rgb.format.RGBFormatter;
import me.neznamy.tab.api.chat.rgb.format.UnnamedFormat1;
import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
@@ -44,7 +45,8 @@ public class RGBUtils {
new BukkitFormat(),
new CMIFormat(),
new UnnamedFormat1(),
new HtmlFormat()
new HtmlFormat(),
new KyoriFormat()
};
gradients = new GradientPattern[] {
//{#RRGGBB>}text{#RRGGBB<}
@@ -72,21 +74,16 @@ public class RGBUtils {
/**
* Applies all RGB formats and gradients to text and returns it.
* This method is called on every property initialization to convert formats
* once instead of on every refresh, if possible. Because of that, parameter
* {@code ignorePlaceholders} exists to not break raw placeholder identifiers
*
* @param text
* original text
* @param ignorePlaceholders
* whether placeholders should be ignored or not
* @return text where everything is converted to #RRGGBB
*/
public String applyFormats(String text, boolean ignorePlaceholders) {
public String applyFormats(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, ignorePlaceholders);
replaced = pattern.applyPattern(replaced, false);
}
for (RGBFormatter formatter : formats) {
replaced = formatter.reformat(replaced);
@@ -94,6 +91,23 @@ public class RGBUtils {
return replaced;
}
/**
* Applies all gradient formats to text and returns it. This only affects
* usage where no placeholder is used inside.
*
* @param text
* original text
* @return text where all gradients with static text are converted to #RRGGBB
*/
public String applyCleanGradients(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, true);
}
return replaced;
}
/**
* Converts TAB's RGB format (#RRGGBB) into bukkit one
* (&amp;x&amp;r&amp;r&amp;g&amp;g&amp;b&amp;b) for modern
@@ -110,7 +124,7 @@ public class RGBUtils {
if (!text.contains("#")) return text; //no rgb codes
if (rgbClient) {
//converting random formats to TAB one
String replaced = applyFormats(text, false);
String replaced = applyFormats(text);
for (Pattern p : new Pattern[]{tabPatternLegacy, tabPattern}) {
Matcher m = p.matcher(replaced);
while (m.find()) {
@@ -135,7 +149,7 @@ public class RGBUtils {
public String convertRGBtoLegacy(String text) {
if (text == null) return null;
if (!text.contains("#")) return EnumChatFormat.color(text);
String applied = applyFormats(text, false);
String applied = applyFormats(text);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < applied.length(); i++){
char c = applied.charAt(i);
@@ -0,0 +1,25 @@
package me.neznamy.tab.api.chat.rgb.format;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Formatter for &lt;#RRGGBB>
*/
public class KyoriFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("<#[0-9a-fA-F]{6}>");
@Override
public String reformat(String text) {
if (!text.contains("<#")) return text;
String replaced = text;
Matcher m = pattern.matcher(replaced);
while (m.find()) {
String hexCode = m.group();
String fixed = hexCode.substring(2, 8);
replaced = replaced.replace(hexCode, "#" + fixed);
}
return replaced;
}
}
@@ -6,10 +6,6 @@ public interface Placeholder {
String getIdentifier();
boolean isTriggerMode();
void enableTriggerMode();
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
void unload();
@@ -68,6 +68,4 @@ public interface ThreadManager {
* @return future allowing to cancel the task
*/
Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, Runnable task);
}
@@ -1,5 +1,7 @@
package me.neznamy.tab.api.util;
import me.neznamy.tab.api.TabPlayer;
public class Preconditions {
private Preconditions(){}
@@ -17,4 +19,8 @@ public class Preconditions {
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + ")");
}
public static void checkLoaded(TabPlayer player) {
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ val platforms = setOf(
projects.bukkit,
projects.bungeecord,
// projects.krypton,
projects.velocity
// projects.velocity
).map { it.dependencyProject }
val special = setOf(
+3 -1
View File
@@ -7,5 +7,7 @@ dependencies {
compileOnly(libs.via)
compileOnly(libs.authlib)
compileOnly(libs.libsDisguises)
compileOnly(libs.essentials)
compileOnly(libs.essentials) {
exclude("org.spigotmc", "spigot-api")
}
}
@@ -15,8 +15,15 @@ import me.neznamy.tab.shared.TAB;
*/
public class BukkitEventListener implements Listener {
/** Platform instance */
private final BukkitPlatform platform;
/**
* Constructs new instance with given parameter
*
* @param platform
* Platform instance
*/
public BukkitEventListener(BukkitPlatform platform) {
this.platform = platform;
}
@@ -6,26 +6,21 @@ import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.api.chat.WrappedChatComponent;
import me.neznamy.tab.api.protocol.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
import me.neznamy.tab.api.chat.ChatComponentEntity;
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
@@ -40,17 +35,17 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitPacketBuilder extends PacketBuilder {
//nms storage
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
//entity type ids
/** Map of entity type ids for entities used by the plugin */
private final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
/** Component cache for better performance (1.16+ players) */
private final Map<IChatBaseComponent, Object> componentCacheModern = new HashMap<>();
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
private Object emptyScoreboard;
private Object dummyEntity;
/** Component cache for better performance (1.15- players) */
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
/**
* Constructs new instance
@@ -65,18 +60,6 @@ public class BukkitPacketBuilder extends PacketBuilder {
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
try {
emptyScoreboard = nms.newScoreboard.newInstance();
} catch (ReflectiveOperationException e) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Failed to create instance of \"Scoreboard\""));
}
if (nms.getMinorVersion() >= 8) {
try {
dummyEntity = nms.newEntityArmorStand.newInstance(nms.World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
} catch (ReflectiveOperationException e) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Failed to create instance of \"EntityArmorStand\""));
}
}
buildMap.put(PacketPlayOutEntityMetadata.class, (packet, version) -> build((PacketPlayOutEntityMetadata)packet));
buildMap.put(PacketPlayOutEntityTeleport.class, (packet, version) -> build((PacketPlayOutEntityTeleport)packet));
buildMap.put(PacketPlayOutEntityDestroy.class, (packet, version) -> build((PacketPlayOutEntityDestroy)packet));
@@ -108,7 +91,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (nms.getMinorVersion() == 7) {
return nms.newPacketPlayOutChat.newInstance(component);
}
throw new IllegalStateException("Not supported on <1.7");
return packet;
}
@Override
@@ -178,7 +161,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
}
Object score = nms.newScoreboardScore.newInstance(emptyScoreboard, newScoreboardObjective(packet.getObjectiveName()), packet.getPlayer());
Object score = nms.newScoreboardScore.newInstance(nms.emptyScoreboard, newScoreboardObjective(packet.getObjectiveName()), packet.getPlayer());
nms.ScoreboardScore_setScore.invoke(score, packet.getScore());
if (nms.getMinorVersion() >= 8) {
return nms.newPacketPlayOutScoreboardScore.newInstance(score);
@@ -189,7 +172,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.PacketPlayOutScoreboardTeam == null) return null; //fabric
Object team = nms.newScoreboardTeam.newInstance(emptyScoreboard, packet.getName());
Object team = nms.newScoreboardTeam.newInstance(nms.emptyScoreboard, packet.getName());
String prefix = packet.getPlayerPrefix();
String suffix = packet.getPlayerSuffix();
if (clientVersion.getMinorVersion() < 13) {
@@ -224,9 +207,11 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity destroy packet with given parameter
* Builds entity destroy packet from custom packet class
*
* @return destroy packet
* @param packet
* Destroy packet
* @return NMS destroy packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@@ -239,21 +224,32 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
}
/**
* Builds entity metadata packet from custom packet class
*
* @param packet
* Metadata packet
* @return NMS metadata packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
}
/**
* Builds entity spawn packet with given parameters
* Builds entity spawn packet from custom packet class
*
* @return entity spawn packet
* @param packet
* Spawn packet
* @return NMS spawn packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutSpawnEntityLiving packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(dummyEntity);
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance();
}
@@ -278,16 +274,18 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity teleport packet with given parameters
* Builds entity teleport packet from custom packet class
*
* @return entity teleport packet
* @param packet
* Teleport packet
* @return NMS teleport packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityTeleport packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(dummyEntity);
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance();
}
@@ -305,7 +303,23 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_PITCH, (byte) (packet.getLocation().getPitch()/360*256));
return nmsPacket;
}
/**
* Writes data into NMS team from custom team packet. Used on 1.13+ servers.
*
* @param packet
* Packet to read from
* @param clientVersion
* Version of player
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private void createTeamModern(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
@@ -315,6 +329,20 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
}
/**
* Writes data into NMS team from custom team packet. Used on 1.12- servers.
*
* @param packet
* Packet to read from
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
@@ -332,7 +360,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : fromNMSComponent(nmsComponent);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = (Property) profile.getProperties().get("textures").iterator().next();
@@ -408,69 +436,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Converts minecraft IChatBaseComponent into TAB's component class. Currently, does not support show_item hover event on 1.16+.
*
* @param component
* component to convert
* @return converted component
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private IChatBaseComponent fromNMSComponent(Object component) throws ReflectiveOperationException {
if (!nms.ChatComponentText.isInstance(component)) return null; //paper
IChatBaseComponent chat = new IChatBaseComponent((String) nms.ChatComponentText_text.get(component));
Object modifier = nms.ChatBaseComponent_modifier.get(component);
if (modifier != null) {
chat.getModifier().setColor(fromNMSColor(nms.ChatModifier_color.get(modifier)));
chat.getModifier().setBold((Boolean) nms.ChatModifier_bold.get(modifier));
chat.getModifier().setItalic((Boolean) nms.ChatModifier_italic.get(modifier));
chat.getModifier().setObfuscated((Boolean) nms.ChatModifier_obfuscated.get(modifier));
chat.getModifier().setStrikethrough((Boolean) nms.ChatModifier_strikethrough.get(modifier));
chat.getModifier().setUnderlined((Boolean) nms.ChatModifier_underlined.get(modifier));
Object clickEvent = nms.ChatModifier_clickEvent.get(modifier);
if (clickEvent != null) {
chat.getModifier().onClick(EnumClickAction.valueOf(nms.ChatClickable_action.get(clickEvent).toString().toUpperCase()), (String) nms.ChatClickable_value.get(clickEvent));
}
Object hoverEvent = nms.ChatModifier_hoverEvent.get(modifier);
if (hoverEvent != null) {
EnumHoverAction action;
IChatBaseComponent value;
if (nms.getMinorVersion() >= 16) {
//does not support show_item on 1.16+
JsonObject json = (JsonObject) nms.ChatHoverable_serialize.invoke(hoverEvent);
action = EnumHoverAction.valueOf(json.get("action").getAsString().toUpperCase());
value = IChatBaseComponent.deserialize(json.get("contents").getAsJsonObject().toString());
} else {
action = EnumHoverAction.valueOf(nms.ChatHoverable_getAction.invoke(hoverEvent).toString().toUpperCase());
value = fromNMSComponent(nms.ChatHoverable_getValue.invoke(hoverEvent));
}
chat.getModifier().onHover(action, value);
}
}
for (Object extra : (List<Object>) nms.ChatBaseComponent_extra.get(component)) {
chat.addExtra(fromNMSComponent(extra));
}
return chat;
}
private TextColor fromNMSColor(Object color) throws ReflectiveOperationException {
if (color == null) return null;
if (nms.getMinorVersion() >= 16) {
String name = (String) nms.ChatHexColor_name.get(color);
if (name != null) {
//legacy code
return new TextColor(EnumChatFormat.valueOf(name.toUpperCase(Locale.US)));
} else {
int rgb = (int) nms.ChatHexColor_rgb.get(color);
return new TextColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
}
} else {
return new TextColor(EnumChatFormat.valueOf(((Enum)color).name()));
}
}
/**
* Converts TAB's IChatBaseComponent into minecraft's component. Currently, does not support hover event.
* Converts TAB's IChatBaseComponent into minecraft's component.
*
* @param component
* component to convert
@@ -481,27 +447,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
* if thrown by reflective operation
*/
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Object obj;
if (clientVersion.getMinorVersion() >= 16) {
if (componentCacheModern.containsKey(component)) return componentCacheModern.get(component);
obj = toNMSComponent0(component, clientVersion);
if (componentCacheModern.size() > 10000) componentCacheModern.clear();
componentCacheModern.put(component, obj);
} else {
if (componentCacheLegacy.containsKey(component)) return componentCacheLegacy.get(component);
obj = toNMSComponent0(component, clientVersion);
if (componentCacheLegacy.size() > 10000) componentCacheLegacy.clear();
componentCacheLegacy.put(component, obj);
}
return obj;
}
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
private Object toNMSComponent0(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (component == null) return null;
if (component instanceof WrappedChatComponent) return ((WrappedChatComponent) component).get();
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
if (cache.containsKey(component)) return cache.get(component);
if (cache.size() > 10000) cache.clear();
Object chat = nms.newChatComponentText.newInstance(component.getText());
Object modifier;
Object clickEvent = component.getModifier().getClickEvent() == null ? null : nms.newChatClickable.newInstance(Enum.valueOf((Class<Enum>) nms.EnumClickAction,
Object clickEvent = component.getModifier().getClickEvent() == null ? null : nms.newChatClickable.newInstance(Enum.valueOf((Class<Enum>) nms.EnumClickAction,
component.getModifier().getClickEvent().getAction().toString().toUpperCase()), component.getModifier().getClickEvent().getValue());
if (nms.getMinorVersion() >= 16) {
Object color = null;
@@ -516,17 +469,17 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (component.getModifier().getHoverEvent() != null) {
Object nmsAction = nms.EnumHoverAction_a.invoke(null, component.getModifier().getHoverEvent().getAction().toString().toLowerCase());
switch (component.getModifier().getHoverEvent().getAction()) {
case SHOW_TEXT:
hoverEvent = nms.newChatHoverable.newInstance(nmsAction, toNMSComponent0(component.getModifier().getHoverEvent().getValue(), clientVersion));
break;
case SHOW_ENTITY:
hoverEvent = nms.EnumHoverAction_fromJson.invoke(nmsAction, ((ChatComponentEntity) component.getModifier().getHoverEvent().getValue()).toJson());
break;
case SHOW_ITEM:
hoverEvent = nms.EnumHoverAction_fromLegacyComponent.invoke(nmsAction, toNMSComponent0(component.getModifier().getHoverEvent().getValue(), clientVersion));
break;
default:
break;
case SHOW_TEXT:
hoverEvent = nms.newChatHoverable.newInstance(nmsAction, toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion));
break;
case SHOW_ENTITY:
hoverEvent = nms.EnumHoverAction_fromJson.invoke(nmsAction, ((ChatComponentEntity) component.getModifier().getHoverEvent().getValue()).toJson());
break;
case SHOW_ITEM:
hoverEvent = nms.EnumHoverAction_fromLegacyComponent.invoke(nmsAction, toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion));
break;
default:
break;
}
}
modifier = nms.newChatModifier.newInstance(
@@ -547,17 +500,27 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.setField(modifier, nms.ChatModifier_obfuscated, component.getModifier().getObfuscated());
if (clickEvent != null) nms.setField(modifier, nms.ChatModifier_clickEvent, clickEvent);
if (component.getModifier().getHoverEvent() != null) {
nms.setField(modifier, nms.ChatModifier_hoverEvent, nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null,
component.getModifier().getHoverEvent().getAction().toString().toLowerCase()), toNMSComponent0(component.getModifier().getHoverEvent().getValue(), clientVersion)));
nms.setField(modifier, nms.ChatModifier_hoverEvent, nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null,
component.getModifier().getHoverEvent().getAction().toString().toLowerCase()), toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion)));
}
}
nms.setField(chat, nms.ChatBaseComponent_modifier, modifier);
for (IChatBaseComponent extra : component.getExtra()) {
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent0(extra, clientVersion));
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent(extra, clientVersion));
}
cache.put(component, chat);
return chat;
}
/**
* Creates a new Scoreboard Objective with given name.
*
* @param objectiveName
* Objective name
* @return NMS Objective
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object newScoreboardObjective(String objectiveName) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, nms.newChatComponentText.newInstance(""), null);
@@ -9,16 +9,18 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PipelineInjector;
import org.jetbrains.annotations.NotNull;
/**
* Pipeline injection for bukkit
*/
public class BukkitPipelineInjector extends PipelineInjector {
//nms storage
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
/**
@@ -38,7 +40,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
*/
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
/** Injected player */
private final TabPlayer player;
/**
@@ -48,7 +50,6 @@ public class BukkitPipelineInjector extends PipelineInjector {
* player to inject
*/
public BukkitChannelDuplexHandler(TabPlayer player) {
Preconditions.checkNotNull(player, "player");
this.player = player;
}
@@ -70,7 +71,9 @@ public class BukkitPipelineInjector extends PipelineInjector {
return;
}
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
long time = System.nanoTime();
modifyPlayers(packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
super.write(context, packet, channelPromise);
return;
}
@@ -101,7 +104,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
*/
@SuppressWarnings("unchecked")
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
long time = System.nanoTime();
int action = nms.PacketPlayOutScoreboardTeam_ACTION.getInt(packetPlayOutScoreboardTeam);
if (action == 1 || action == 2 || action == 4) return;
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
if (players == null) return;
@@ -121,9 +125,17 @@ public class BukkitPipelineInjector extends PipelineInjector {
}
}
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
/**
* Returns player with matching game profile name. This is different from
* real name when a nick plugin changing names of players is used. If no
* player was found, returns {@code null}.
*
* @param name
* Game profile name
* @return Player with matching game profile name
*/
private TabPlayer getPlayer(String name) {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.getNickname().equals(name)) return p;
@@ -5,6 +5,7 @@ import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.Locale;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@@ -13,7 +14,6 @@ import org.bukkit.plugin.RegisteredServiceProvider;
import com.earth2me.essentials.Essentials;
import me.neznamy.tab.api.placeholder.PlaceholderManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
import net.milkbowl.vault.chat.Chat;
@@ -25,26 +25,22 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
/** Number formatter for 2 decimal places */
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
private Object chat;
private final Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");
private Object chat; // Object because Fabric
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
private Object server;
private Field recentTps;
private boolean paperTps;
private boolean paperMspt;
private Method playerIsAfk;
private Method paperTps;
private Method paperMspt;
private Method purpurIsAfk;
/**
* Constructs new instance with given parameter
*/
public BukkitPlaceholderRegistry() {
numberFormat.setMaximumFractionDigits(2);
try {
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Class.forName("net.milkbowl.vault.chat.Chat"));
if (rspChat != null) chat = rspChat.getProvider();
}
} catch (ClassNotFoundException e) {
//modded server without vault
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
if (rspChat != null) chat = rspChat.getProvider();
}
try {
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
@@ -52,70 +48,50 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
} catch (ReflectiveOperationException e) {
//not spigot
}
try {
Bukkit.class.getMethod("getTPS");
paperTps = true;
} catch (NoSuchMethodException e) {
//not paper
}
try {
Bukkit.class.getMethod("getAverageTickTime");
paperMspt = true;
} catch (NoSuchMethodException e) {
//not paper
}
try {
playerIsAfk = Player.class.getMethod("isAfk");
} catch (NoSuchMethodException e) {
//not purpur
}
try { paperTps = Bukkit.class.getMethod("getTPS"); } catch (NoSuchMethodException ignored) {}
try { paperMspt = Bukkit.class.getMethod("getAverageTickTime"); } catch (NoSuchMethodException ignored) {}
try { purpurIsAfk = Player.class.getMethod("isAfk"); } catch (NoSuchMethodException ignored) {}
}
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
super.registerPlaceholders(manager);
manager.registerPlayerPlaceholder("%displayname%", 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps) {
manager.registerServerPlaceholder("%tps%", 1000, () -> formatTPS(Bukkit.getTPS()[0]));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> formatTPS(Bukkit.getTPS()[0]));
} else if (recentTps != null) {
manager.registerServerPlaceholder("%tps%", 1000, () -> {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> {
try {
return formatTPS(((double[]) recentTps.get(server))[0]);
} catch (IllegalAccessException e) {
return "-1";
return -1;
}
});
} else {
manager.registerServerPlaceholder("%tps%", -1, () -> "-1").enableTriggerMode();
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, -1, () -> -1);
}
if (paperMspt) {
manager.registerServerPlaceholder("%mspt%", 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
if (paperMspt != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
}
manager.registerPlayerPlaceholder("%afk%", 500, p -> {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
if (playerIsAfk == null) return false;
try {
return playerIsAfk.invoke(p.getPlayer());
} catch (ReflectiveOperationException exception) {
TAB.getInstance().getErrorManager().printError("Failed to get AFK status of " + p.getName() + " using Purpur", exception);
}
return false;
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
});
manager.registerPlayerPlaceholder("%essentialsnick%", 1000, p -> {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, 1000, p -> {
String nickname = null;
if (essentials != null)
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
});
if (chat != null) {
manager.registerPlayerPlaceholder("%vault-prefix%", 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder("%vault-suffix%", 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
} else {
manager.registerServerPlaceholder("%vault-prefix%", -1, () -> "").enableTriggerMode();
manager.registerServerPlaceholder("%vault-suffix%", -1, () -> "").enableTriggerMode();
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, -1, () -> "");
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
}
manager.registerPlayerPlaceholder("%health%", 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
}
private String formatTPS(double tps) {
@@ -4,7 +4,7 @@ import com.earth2me.essentials.Essentials;
import com.viaversion.viaversion.api.Via;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
@@ -41,11 +41,11 @@ public class BukkitPlatform extends Platform {
private final JavaPlugin plugin;
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
private final Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
private Plugin viaVersion;
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport");
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
/**
* Constructs new instance with given plugin parameter
@@ -55,18 +55,17 @@ public class BukkitPlatform extends Platform {
*/
public BukkitPlatform(JavaPlugin plugin) {
super(new BukkitPacketBuilder());
Preconditions.checkNotNull(plugin, "plugin");
this.plugin = plugin;
}
@Override
public PermissionPlugin detectPermissionPlugin() {
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
return new LuckPerms(getPluginVersion("LuckPerms"));
} else if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LUCKPERMS)) {
return new LuckPerms(getPluginVersion(TabConstants.Plugin.LUCKPERMS));
} else if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider == null) return new None();
return new Vault(provider.getProvider(), getPluginVersion("Vault"));
return new Vault(provider.getProvider(), getPluginVersion(TabConstants.Plugin.VAULT));
} else {
return new None();
}
@@ -74,22 +73,16 @@ public class BukkitPlatform extends Platform {
@Override
public void loadFeatures() {
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
try {
Class.forName("com.viaversion.viaversion.api.Via");
viaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion");
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
} catch (ClassNotFoundException e) {
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion("ViaVersion") + ") was detected.", true);
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);
}
}
if (Bukkit.getPluginManager().isPluginEnabled("Tablisknu")) {
TAB.getInstance().sendConsoleMessage("&cDetected plugin \"Tablisknu\", which causes TAB to not work properly. Consider removing the plugin.", true);
}
if (Bukkit.getPluginManager().isPluginEnabled("SkBee")) {
TAB.getInstance().sendConsoleMessage("&cDetected plugin \"SkBee\", which causes TAB's scoreboard to not show. Consider removing the plugin.", true);
}
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
@@ -125,7 +118,6 @@ public class BukkitPlatform extends Platform {
@Override
public String getPluginVersion(String plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
return pl == null ? null : pl.getDescription().getVersion();
}
@@ -154,7 +146,6 @@ public class BukkitPlatform extends Platform {
@Override
public void registerUnknownPlaceholder(String identifier) {
Preconditions.checkNotNull(identifier, "identifier");
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
if (identifier.startsWith("%rel_")) {
//relational placeholder
@@ -231,11 +222,6 @@ public class BukkitPlatform extends Platform {
return (Essentials) essentials;
}
@Override
public boolean isProxy() {
return false;
}
@Override
public String getConfigName() {
return "bukkitconfig.yml";
@@ -271,7 +257,7 @@ public class BukkitPlatform extends Platform {
try {
Object protocolVersion = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class).invoke(null, player);
int version = (int) protocolVersion.getClass().getMethod("getId").invoke(protocolVersion);
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ProtocolSupport", player.getName()), e);
@@ -298,7 +284,7 @@ public class BukkitPlatform extends Platform {
Thread.sleep(5);
return getProtocolVersionVia(player, retryLevel + 1);
}
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -308,4 +294,18 @@ public class BukkitPlatform extends Platform {
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
/**
* Sends console message using ConsoleCommandSender, due to
* Paper not translating colors correctly in Logger messages
*
* @param message
* Message to send
* @param translateColors
* Whether color codes should be translated or not
*/
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.bukkit;
import java.util.*;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.util.Preconditions;
import org.bukkit.Bukkit;
@@ -95,6 +96,8 @@ public class BukkitTabPlayer extends ITabPlayer {
} else {
handleVia((PacketPlayOutBoss) nmsPacket);
}
} else if (nmsPacket instanceof PacketPlayOutChat) {
getPlayer().sendMessage(((PacketPlayOutChat) nmsPacket).getMessage().toLegacyText());
} else {
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
}
@@ -26,24 +26,25 @@ public class Main extends JavaPlugin {
@Override
public void onEnable(){
getLogger().info(EnumChatFormat.color("&7Server version: " + Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")"));
String version = Bukkit.getBukkitVersion().split("-")[0];
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Server version: " + version + " (" + serverPackage + ")"));
if (!isVersionSupported()){
Bukkit.getPluginManager().disablePlugin(this);
return;
}
BukkitPlatform platform = new BukkitPlatform(this);
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]),
Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")", getDataFolder(), getLogger()));
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(version), version + " (" + serverPackage + ")", getDataFolder(), getLogger()));
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
getLogger().info(EnumChatFormat.color("&cUnknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
}
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(platform), this);
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 5304);
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("placeholderapi", () -> Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("server_version", () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.UNLIMITED_NAME_TAG_MODE_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PLACEHOLDER_API, () -> Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION, () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
PluginCommand cmd = Bukkit.getPluginCommand("tab");
if (cmd == null) return;
TABCommand command = new TABCommand();
@@ -53,7 +54,7 @@ public class Main extends JavaPlugin {
@Override
public void onDisable() {
//null check due to L31 return making L34 not run
//null check due to L34 return making L37 not run
if (TAB.getInstance() != null) TAB.getInstance().unload();
}
@@ -75,16 +76,17 @@ public class Main extends JavaPlugin {
long time = System.currentTimeMillis();
NMSStorage.setInstance(new NMSStorage());
if (supportedVersions.contains(serverPackage)) {
getLogger().info(EnumChatFormat.color("&7Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} else {
getLogger().info(EnumChatFormat.color("&cNo 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.x - 1.18.2. 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 1.5.x - 1.18.2. Disabling just to stay safe."));
}
} catch (Exception ex) {
if (supportedVersions.contains(serverPackage)) {
getLogger().log(Level.SEVERE, EnumChatFormat.color("&cYour server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"), ex);
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
getLogger().log(Level.SEVERE, "", ex);
} else {
getLogger().info(EnumChatFormat.color("&cYour server version is completely unsupported. This plugin version only supports 1.5.x - 1.18.2. Disabling."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.18.2. Disabling."));
}
}
return false;
@@ -93,7 +95,7 @@ public class Main extends JavaPlugin {
/**
* Command handler for /tab command
*/
public static class TABCommand implements CommandExecutor, TabCompleter {
private static class TABCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
@@ -9,7 +9,6 @@ import me.neznamy.tab.shared.features.TabExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
@@ -27,11 +26,6 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
return true;
}
@Override
public boolean canRegister(){
return true;
}
@Override
public @NotNull String getAuthor(){
return "NEZNAMY";
@@ -49,51 +43,22 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
@Override
public String onPlaceholderRequest(Player player, @NotNull String identifier){
if (player == null) return "";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (identifier.startsWith("replace_")) {
String placeholder = "%" + identifier.substring(8) + "%";
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
return TAB.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
}
if (identifier.startsWith("placeholder_")) {
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList("%" + identifier.substring(12) + "%"));
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", TAB.getInstance().getPlaceholderManager());
}
return values.computeIfAbsent(p, pl -> new HashMap<>()).get(identifier);
if (player == null) return "<Player cannot be null>";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
return values.get(p).get(identifier);
}
@Override
public void setScoreboardVisible(TabPlayer player, boolean visible) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("scoreboard_visible", visible ? "Enabled" : "Disabled");
}
@Override
public void setScoreboardName(TabPlayer player, String name) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("scoreboard_name", name);
}
@Override
public void setBossBarVisible(TabPlayer player, boolean visible) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("bossbar_visible", visible ? "Enabled" : "Disabled");
}
@Override
public void setNameTagPreview(TabPlayer player, boolean previewing) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("ntpreview", previewing ? "Enabled" : "Disabled");
}
@Override
public void setPlaceholderValue(TabPlayer player, String placeholder, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("placeholder_" + placeholder.substring(1, placeholder.length()-1), value);
}
@Override
public void setPropertyValue(TabPlayer player, String property, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(property, value);
}
@Override
public void setRawPropertyValue(TabPlayer player, String property, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(property + "_raw", value);
public void setValue(TabPlayer player, String key, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
}
}
@@ -80,15 +80,13 @@ public class PetFix extends TabFeature {
*/
@Override
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayInUseEntity.isInstance(packet)) {
if (lastInteractFix.containsKey(sender) && (System.currentTimeMillis() - lastInteractFix.get(sender) < INTERACT_COOLDOWN)) {
if (nms.PacketPlayInUseEntity.isInstance(packet) && isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
if (System.currentTimeMillis() - lastInteractFix.getOrDefault(sender, 0L) < INTERACT_COOLDOWN) {
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
return true;
}
if (isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender, System.currentTimeMillis());
}
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender, System.currentTimeMillis());
}
return false;
}
@@ -1,6 +1,5 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.api.util.Preconditions;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -32,7 +31,6 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
* plugin instance
*/
public WitherBossBar(JavaPlugin plugin) {
Preconditions.checkNotNull(plugin, "plugin");
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
@@ -337,12 +337,8 @@ public class BukkitArmorStand implements ArmorStand {
@Override
public void respawn(TabPlayer viewer) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
Runnable spawn = () -> spawn(viewer);
if (viewer.getVersion().getMinorVersion() == 8) {
//1.8.0 client sided bug
TAB.getInstance().getCPUManager().runTaskLater(50, manager, TabConstants.CpuUsageCategory.V1_8_0_BUG_COMPENSATION, spawn);
} else {
spawn.run();
}
// 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);
}
}
@@ -54,6 +54,13 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.teleport(viewer);
}
/**
* Teleports armor stands to player's current location for all nearby players
*/
public void teleport() {
for (ArmorStand a : armorStandArray) a.teleport();
}
/**
* Returns array of nearby players
* @return array of nearby players
@@ -99,13 +106,6 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.sneak(sneaking);
}
/**
* Teleports armor stands to player's current location for all nearby players
*/
public void teleport() {
for (ArmorStand a : armorStandArray) a.teleport();
}
/**
* Performs respawn operation on all armor stands to skip teleport animation
*/
@@ -130,17 +130,6 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.spawn(viewer);
}
/**
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
*
* @param viewer
* player to destroy armor stands for
*/
public void destroy(TabPlayer viewer) {
for (ArmorStand a : armorStandArray) a.destroy(viewer);
unregisterPlayer(viewer);
}
/**
* Fixes heights of all armor stands due to dynamic lines
*/
@@ -169,30 +158,6 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
}
/**
* Gets armor stand by name
*
* @param name
* Name to get armor stand by
* @return Armor stand with given name or null if not found
*/
public ArmorStand getArmorStand(String name) {
return armorStands.get(name);
}
/**
* Removes armor stand by given name if exists
*
* @param name
* Name of line to remove
*/
public void removeArmorStand(String name) {
if (!armorStands.containsKey(name)) return;
armorStands.get(name).destroy();
armorStands.remove(name);
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
}
/**
* Removes specified player from list of nearby players
*
@@ -207,6 +172,17 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.updateVisibility(force);
}
/**
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
*
* @param viewer
* player to destroy armor stands for
*/
public void destroy(TabPlayer viewer) {
for (ArmorStand a : armorStandArray) a.destroy(viewer);
unregisterPlayer(viewer);
}
@Override
public void destroy() {
for (ArmorStand a : armorStandArray) a.destroy();
@@ -134,6 +134,12 @@ public class BukkitNameTagX extends NameTagX {
if (isPreviewingNametag(p)) {
getArmorStandManager(p).spawn(p);
}
//for some reason this is needed for some users
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getWorld().equals(from)) {
getArmorStandManager(p).destroy(viewer);
}
}
}
/**
@@ -33,7 +33,6 @@ public class VehicleRefresher extends TabFeature {
for (TabPlayer inVehicle : playersInVehicle.keySet()) {
if (!inVehicle.isOnline() || feature.getArmorStandManager(inVehicle) == null) continue; // not removed from WeakHashMap yet
feature.getArmorStandManager(inVehicle).teleport();
// feature.getVehicleManager().processPassengers((Entity) inVehicle.getPlayer());
}
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (feature.isPreviewingNametag(p)) {
@@ -41,8 +40,8 @@ public class VehicleRefresher extends TabFeature {
}
}
});
addUsedPlaceholders(Collections.singletonList("%vehicle%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VEHICLE));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
}
@Override
@@ -126,19 +125,4 @@ public class VehicleRefresher extends TabFeature {
}
}
}
/**
* Teleports armor stands of all passengers on specified vehicle
*
* @param vehicle
* entity to check passengers of
*/
public void processPassengers(Entity vehicle) {
for (Entity passenger : getPassengers(vehicle)) {
if (passenger instanceof Player) {
feature.getArmorStandManager(TAB.getInstance().getPlayer(passenger.getUniqueId())).teleport();
}
processPassengers(passenger);
}
}
}
@@ -15,7 +15,6 @@ import java.util.UUID;
import org.bukkit.Bukkit;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
@@ -46,7 +45,6 @@ public final class NMSStorage {
private final Class<?> Entity = getNMSClass("net.minecraft.world.entity.Entity", "Entity");
private final Class<?> EntityLiving = getNMSClass("net.minecraft.world.entity.EntityLiving", "EntityLiving");
private final Class<?> PlayerConnection = getNMSClass("net.minecraft.server.network.PlayerConnection", "PlayerConnection");
public Constructor<?> newEntityArmorStand;
public final Field PING = getField(EntityPlayer, "ping", "latency", "field_71138_i", "field_13967", "e");
public final Field PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
public Field NETWORK_MANAGER;
@@ -54,23 +52,16 @@ public final class NMSStorage {
public final Method getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
public final Method sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
public Method getProfile;
public final Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".CraftWorld").getMethod("getHandle");
public final Enum[] EnumChatFormat_values = getEnumValues(EnumChatFormat);
//chat
public Class<?> ChatComponentText;
public Class<?> ChatHoverable;
public Class<?> EnumClickAction;
private Class<?> IChatBaseComponent;
public Constructor<?> newChatComponentText;
public Constructor<?> newChatClickable;
public Constructor<?> newChatModifier;
public Constructor<?> newChatHoverable;
public Field ChatBaseComponent_extra;
public Field ChatBaseComponent_modifier;
public Field ChatComponentText_text;
public Field ChatClickable_action;
public Field ChatClickable_value;
public Field ChatModifier_bold;
public Field ChatModifier_italic;
public Field ChatModifier_underlined;
@@ -78,16 +69,11 @@ public final class NMSStorage {
public Field ChatModifier_obfuscated;
public Field ChatModifier_clickEvent;
public Field ChatModifier_hoverEvent;
public Field ChatHexColor_name;
public Field ChatHexColor_rgb;
public Field ChatModifier_color;
public Method ChatComponentText_addSibling;
public Method EnumHoverAction_a;
public Method ChatHoverable_getAction;
public Method ChatHexColor_ofInt;
public Method ChatHexColor_ofString;
public Method ChatHoverable_serialize;
public Method ChatHoverable_getValue;
public Method EnumHoverAction_fromJson;
public Method EnumHoverAction_fromLegacyComponent;
@@ -182,7 +168,7 @@ public final class NMSStorage {
private final Class<?> IScoreboardCriteria = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria", "IScoreboardCriteria");
public Class<?> EnumScoreboardHealthDisplay;
public final Constructor<?> newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
public final Constructor<?> newScoreboard = Scoreboard.getConstructor();
private final Constructor<?> newScoreboard = Scoreboard.getConstructor();
public final Constructor<?> newScoreboardScore = ScoreboardScore.getConstructor(Scoreboard, ScoreboardObjective, String.class);
public final Constructor<?> newPacketPlayOutScoreboardDisplayObjective = PacketPlayOutScoreboardDisplayObjective.getConstructor(int.class, ScoreboardObjective);
public Constructor<?> newPacketPlayOutScoreboardObjective;
@@ -205,6 +191,7 @@ public final class NMSStorage {
public Constructor<?> newScoreboardTeam;
public Constructor<?> newPacketPlayOutScoreboardTeam;
public Field PacketPlayOutScoreboardTeam_NAME;
public Field PacketPlayOutScoreboardTeam_ACTION;
public Field PacketPlayOutScoreboardTeam_PLAYERS;
public Method ScoreboardTeam_getPlayerNameSet;
public Method ScoreboardTeam_setNameTagVisibility;
@@ -221,6 +208,10 @@ public final class NMSStorage {
public Enum[] EnumTeamPush_values;
public Enum[] PacketPlayOutScoreboardTeam_PlayerAction_values;
//other
public final Object emptyScoreboard = newScoreboard.newInstance();
public Object dummyEntity;
/**
* Creates new instance, initializes required NMS classes and fields
*
@@ -236,8 +227,10 @@ public final class NMSStorage {
if (minorVersion >= 8) {
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
getProfile = getMethods(getNMSClass("net.minecraft.world.entity.player.EntityHuman", "EntityHuman"), GameProfile.class).get(0);
newEntityArmorStand = getNMSClass("net.minecraft.world.entity.decoration.EntityArmorStand", "EntityArmorStand")
Constructor<?> newEntityArmorStand = getNMSClass("net.minecraft.world.entity.decoration.EntityArmorStand", "EntityArmorStand")
.getConstructor(getNMSClass("net.minecraft.world.level.World", "World"), double.class, double.class, double.class);
Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".CraftWorld").getMethod("getHandle");
dummyEntity = newEntityArmorStand.newInstance(World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
}
initializeChatComponents();
initializeChatPacket();
@@ -277,19 +270,15 @@ public final class NMSStorage {
if (minorVersion < 7) return;
Class<?> ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
Class<?> ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
Class<?> ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
Class<?> ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
Class<?> ChatModifier = getNMSClass("net.minecraft.network.chat.ChatModifier", "ChatModifier");
EnumClickAction = getNMSClass("net.minecraft.network.chat.ChatClickable$EnumClickAction", "ChatClickable$EnumClickAction", "EnumClickAction");
Class<?> EnumHoverAction = getNMSClass("net.minecraft.network.chat.ChatHoverable$EnumHoverAction", "ChatHoverable$EnumHoverAction", "EnumHoverAction");
IChatBaseComponent = getNMSClass("net.minecraft.network.chat.IChatBaseComponent", "IChatBaseComponent");
newChatComponentText = ChatComponentText.getConstructor(String.class);
newChatClickable = ChatClickable.getConstructor(EnumClickAction, String.class);
ChatBaseComponent_extra = getFields(ChatBaseComponent, List.class).get(0);
ChatBaseComponent_modifier = getFields(ChatBaseComponent, ChatModifier).get(0);
ChatComponentText_text = getFields(ChatComponentText, String.class).get(0);
ChatClickable_action = getFields(ChatClickable, EnumClickAction).get(0);
ChatClickable_value = getFields(ChatClickable, String.class).get(0);
List<Field> booleans = getFields(ChatModifier, Boolean.class);
ChatModifier_bold = booleans.get(0);
ChatModifier_italic = booleans.get(1);
@@ -300,27 +289,20 @@ public final class NMSStorage {
ChatModifier_hoverEvent = getFields(ChatModifier, ChatHoverable).get(0);
ChatComponentText_addSibling = getMethod(ChatComponentText, new String[]{"addSibling", "a", "func_150257_a", "method_10852"}, IChatBaseComponent);
EnumHoverAction_a = getMethod(EnumHoverAction, new String[]{"a", "func_150684_a", "method_27670"}, String.class);
ChatHoverable_getAction = getMethods(ChatHoverable, EnumHoverAction).get(0);
if (minorVersion >= 16) {
Class<?> ChatHexColor = getNMSClass("net.minecraft.network.chat.ChatHexColor", "ChatHexColor");
Class<?> MinecraftKey = getNMSClass("net.minecraft.resources.MinecraftKey", "MinecraftKey");
newChatModifier = setAccessible(ChatModifier.getDeclaredConstructor(ChatHexColor, Boolean.class, Boolean.class, Boolean.class, Boolean.class, Boolean.class, ChatClickable, ChatHoverable, String.class, MinecraftKey));
newChatHoverable = ChatHoverable.getConstructor(EnumHoverAction, Object.class);
List<Field> list = getFields(ChatHexColor, String.class);
ChatHexColor_name = list.get(list.size()-1);
ChatHexColor_rgb = getFields(ChatHexColor, int.class).get(0);
ChatModifier_color = getFields(ChatModifier, ChatHexColor).get(0);
ChatHexColor_ofInt = getMethods(ChatHexColor, ChatHexColor, int.class).get(0);
ChatHexColor_ofString = getMethods(ChatHexColor, ChatHexColor, String.class).get(0);
ChatHoverable_serialize = getMethods(ChatHoverable, JsonObject.class).get(0);
ChatHoverable_getValue = getMethods(ChatHoverable, Object.class, EnumHoverAction).get(0);
EnumHoverAction_fromJson = getMethods(EnumHoverAction, ChatHoverable, JsonElement.class).get(0);
EnumHoverAction_fromLegacyComponent = getMethods(EnumHoverAction, ChatHoverable, IChatBaseComponent).get(0);
} else {
newChatModifier = ChatModifier.getConstructor();
newChatHoverable = ChatHoverable.getConstructor(EnumHoverAction, IChatBaseComponent);
ChatModifier_color = getFields(ChatModifier, EnumChatFormat).get(0);
ChatHoverable_getValue = getMethods(ChatHoverable, IChatBaseComponent).get(0);
}
}
@@ -463,6 +445,7 @@ public final class NMSStorage {
Class<?> ScoreboardTeam = getNMSClass("net.minecraft.world.scores.ScoreboardTeam", "ScoreboardTeam");
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
PacketPlayOutScoreboardTeam_ACTION = getInstanceFields(PacketPlayOutScoreboardTeam, int.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a", "func_96660_a"}, boolean.class);
@@ -637,6 +620,26 @@ public final class NMSStorage {
return list;
}
/**
* Returns all instance fields of class with defined class type
*
* @param clazz
* class to check fields of
* @param type
* field type to check for
* @return list of all fields with specified class type
*/
public List<Field> getInstanceFields(Class<?> clazz, Class<?> type){
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
List<Field> list = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
list.add(setAccessible(field));
}
}
return list;
}
/**
* Returns field with specified name and makes it accessible
*
@@ -4,7 +4,6 @@ import java.util.Optional;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
@@ -30,7 +29,6 @@ public class DataWatcherHelper {
* data to write to
*/
public DataWatcherHelper(DataWatcher data) {
Preconditions.checkNotNull(data, "dataWatcher");
this.data = data;
this.registry = NMSStorage.getInstance().getDataWatcherRegistry();
}
@@ -78,8 +76,6 @@ public class DataWatcherHelper {
* client version
*/
public void setCustomName(String customName, ProtocolVersion clientVersion) {
Preconditions.checkNotNull(customName, "customName");
Preconditions.checkNotNull(clientVersion, "clientVersion");
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 13) {
try {
data.setValue(new DataWatcherObject(2, registry.getOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
@@ -92,12 +88,11 @@ public class DataWatcherHelper {
//name length is limited to 64 characters on <1.8
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6){
data.setValue(new DataWatcherObject(10, registry.getString()), cutName);
data.setValue(new DataWatcherObject(10, null), cutName);
} else {
data.setValue(new DataWatcherObject(5, registry.getString()), cutName);
data.setValue(new DataWatcherObject(5, null), cutName);
}
}
}
/**
@@ -110,7 +105,7 @@ public class DataWatcherHelper {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
data.setValue(new DataWatcherObject(3, registry.getBoolean()), visible);
} else {
data.setValue(new DataWatcherObject(3, registry.getByte()), (byte)(visible?1:0));
data.setValue(new DataWatcherObject(3, null), (byte)(visible?1:0));
}
}
@@ -124,7 +119,7 @@ public class DataWatcherHelper {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6) {
data.setValue(new DataWatcherObject(6, registry.getFloat()), health);
} else {
data.setValue(new DataWatcherObject(16, registry.getInteger()), (int)health);
data.setValue(new DataWatcherObject(16, null), (int)health);
}
}
@@ -13,26 +13,10 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
public class DataWatcherRegistry {
private Object registryByte;
private Object registryShort;
private Object registryInteger;
private Object registryFloat;
private Object registryString;
private Object registryIChatBaseComponent;
private Object registryOptionalIChatBaseComponent;
private Object registryOptionalItemStack;
private Object registryItemStack;
private Object registryOptionalIBlockData;
private Object registryBoolean;
private Object registryParticleParam;
private Object registryVector3f;
private Object registryBlockPosition;
private Object registryOptionalBlockPosition;
private Object registryEnumDirection;
private Object registryOptionalUUID;
private Object registryNBTTagCompound;
private Object registryVillagerData;
private Object registryOptionalInt;
private Object registryEntityPose;
/**
* Initializes required NMS classes and fields
@@ -41,43 +25,13 @@ public class DataWatcherRegistry {
if (nms.getMinorVersion() >= 9) {
Map<String, Object> fields = getStaticFields(nms.DataWatcherRegistry, nms);
registryByte = fields.get("a");
registryInteger = fields.get("b");
registryFloat = fields.get("c");
registryString = fields.get("d");
registryIChatBaseComponent = fields.get("e");
if (nms.getMinorVersion() >= 13) {
registryOptionalIChatBaseComponent = fields.get("f");
registryItemStack = fields.get("g");
registryOptionalIBlockData = fields.get("h");
registryBoolean = fields.get("i");
registryParticleParam = fields.get("j");
registryVector3f = fields.get("k");
registryBlockPosition = fields.get("l");
registryOptionalBlockPosition = fields.get("m");
registryEnumDirection = fields.get("n");
registryOptionalUUID = fields.get("o");
registryNBTTagCompound = fields.get("p");
if (nms.getMinorVersion() >= 15) {
registryVillagerData = fields.get("q");
registryOptionalInt = fields.get("r");
registryEntityPose = fields.get("s");
}
} else {
registryOptionalIBlockData = fields.get("g");
registryBoolean = fields.get("h");
registryVector3f = fields.get("i");
registryBlockPosition = fields.get("j");
registryOptionalBlockPosition = fields.get("k");
registryEnumDirection = fields.get("l");
registryOptionalUUID = fields.get("m");
if (nms.getMinorVersion() >= 12) {
registryNBTTagCompound = fields.get("n");
}
if (nms.getMinorVersion() >= 11) {
registryItemStack = fields.get("f");
} else {
registryOptionalItemStack = fields.get("f");
}
}
}
}
@@ -108,14 +62,6 @@ public class DataWatcherRegistry {
return registryByte;
}
public Object getShort() {
return registryShort;
}
public Object getInteger() {
return registryInteger;
}
public Object getFloat() {
return registryFloat;
}
@@ -124,67 +70,11 @@ public class DataWatcherRegistry {
return registryString;
}
public Object getComponent() {
return registryIChatBaseComponent;
}
public Object getOptionalComponent() {
return registryOptionalIChatBaseComponent;
}
public Object getOptionalItemStack() {
return registryOptionalItemStack;
}
public Object getItemStack() {
return registryItemStack;
}
public Object getOptionalIBlockData() {
return registryOptionalIBlockData;
}
public Object getBoolean() {
return registryBoolean;
}
public Object getParticleParam() {
return registryParticleParam;
}
public Object getVector3f() {
return registryVector3f;
}
public Object getBlockPosition() {
return registryBlockPosition;
}
public Object getOptionalBlockPosition() {
return registryOptionalBlockPosition;
}
public Object getEnumDirection() {
return registryEnumDirection;
}
public Object getOptionalUUID() {
return registryOptionalUUID;
}
public Object getNBTTagCompound() {
return registryNBTTagCompound;
}
public Object getVillagerData() {
return registryVillagerData;
}
public Object getOptionalInt() {
return registryOptionalInt;
}
public Object getEntityPose() {
return registryEntityPose;
}
}
@@ -30,7 +30,7 @@ public class Vault extends PermissionPlugin {
@Override
public String getPrimaryGroup(TabPlayer p) {
if (getName().equals("SuperPerms")) return TabConstants.DEFAULT_GROUP; //Vault's dummy implementation throws exception on every request
if (getName().equals("SuperPerms")) return TabConstants.NO_GROUP; //Vault's dummy implementation throws exception on every request
return permission.getPrimaryGroup((Player) p.getPlayer());
}
+1 -1
View File
@@ -137,7 +137,7 @@ scoreboard:
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
@@ -56,7 +56,7 @@ public class BungeeEventListener implements Listener {
@EventHandler
public void onChat(ChatEvent e) {
if (TAB.getInstance().isDisabled()) return;
if (e.getMessage().startsWith("/") && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
if (e.isCommand() && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
}
/**
@@ -70,8 +70,15 @@ public class BungeeEventListener implements Listener {
if (!event.getTag().equals(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME)) return;
if (event.getReceiver() instanceof ProxiedPlayer) {
event.setCancelled(true);
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(
((ProxiedPlayer) event.getReceiver()).getUniqueId(), event.getData());
TAB.getInstance().getCPUManager().getThreadPool().submit(() -> {
try {
while (TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId()) == null) {
Thread.sleep(100);
}
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(
((ProxiedPlayer) event.getReceiver()).getUniqueId(), event.getData());
} catch (InterruptedException ignored) {}
});
}
}
}
@@ -7,7 +7,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PipelineInjector;
@@ -22,11 +21,15 @@ import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Pipeline injection to secure proper functionality
* of some features by preventing other plugins
* from overriding it.
*/
@SuppressWarnings("unchecked")
public class BungeePipelineInjector extends PipelineInjector {
//packets that must be deserialized and BungeeCord does not do it automatically
/** 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};
@@ -47,7 +50,7 @@ public class BungeePipelineInjector extends PipelineInjector {
*/
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
/** Injected player */
protected final TabPlayer player;
/**
@@ -57,7 +60,6 @@ public class BungeePipelineInjector extends PipelineInjector {
* player to inject
*/
public BungeeChannelDuplexHandler(TabPlayer player) {
Preconditions.checkNotNull(player, "player");
this.player = player;
}
@@ -70,7 +72,9 @@ public class BungeePipelineInjector extends PipelineInjector {
return;
case "Team":
if (antiOverrideTeams) {
long time = System.nanoTime();
modifyPlayers((Team) packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
break;
case "ScoreboardDisplay":
@@ -93,7 +97,7 @@ public class BungeePipelineInjector extends PipelineInjector {
try {
super.write(context, packet, channelPromise);
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
TAB.getInstance().getErrorManager().printError(String.format("Failed to forward packet %s to %s", packet.getClass().getSimpleName(), player.getName()), e);
}
}
@@ -104,8 +108,7 @@ public class BungeePipelineInjector extends PipelineInjector {
* packet to modify
*/
private void modifyPlayers(Team packet){
long time = System.nanoTime();
if (packet.getPlayers() == null) return;
if (packet.getMode() == 1 || packet.getMode() == 2 || packet.getMode() == 4) return;
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (col.contains(p.getNickname()) && !((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
@@ -124,10 +127,14 @@ public class BungeePipelineInjector extends PipelineInjector {
}
}
packet.setPlayers(col.toArray(new String[0]));
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
}
/**
* Channel duplex handler override if features using packets that must be
* deserialized manually are used. If they are disabled, deserialization is
* disabled for better performance.
*/
public class DeserializableBungeeChannelDuplexHandler extends BungeeChannelDuplexHandler {
/**
@@ -144,7 +151,7 @@ public class BungeePipelineInjector extends PipelineInjector {
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
long time = System.nanoTime();
Object modifiedPacket = packet instanceof ByteBuf ? deserialize((ByteBuf) packet) : packet;
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime(TabConstants.Feature.PACKET_DESERIALIZING, TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
super.write(context, modifiedPacket, channelPromise);
}
@@ -1,10 +1,6 @@
package me.neznamy.tab.platforms.bungeecord;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bungeecord.event.TabLoadEvent;
import me.neznamy.tab.platforms.bungeecord.event.TabPlayerLoadEvent;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
@@ -18,7 +14,7 @@ import net.md_5.bungee.api.plugin.Plugin;
public class BungeePlatform extends ProxyPlatform {
/**
* Constructs new instance with given parameter
* Constructs new instance
*/
public BungeePlatform() {
super(new BungeePacketBuilder());
@@ -29,9 +25,9 @@ public class BungeePlatform extends ProxyPlatform {
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
tab.getPlaceholderManager().registerPlayerPlaceholder("%displayname%", 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
tab.getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
super.loadFeatures();
if (ProxyServer.getInstance().getPluginManager().getPlugin("RedisBungee") != null) {
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.REDIS_BUNGEE) != null) {
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport());
} else {
@@ -43,19 +39,8 @@ public class BungeePlatform extends ProxyPlatform {
}
}
@Override
public void callLoadEvent() {
ProxyServer.getInstance().getPluginManager().callEvent(new TabLoadEvent());
}
@Override
public void callLoadEvent(TabPlayer player) {
ProxyServer.getInstance().getPluginManager().callEvent(new TabPlayerLoadEvent(player));
}
@Override
public String getPluginVersion(String plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Plugin pl = ProxyServer.getInstance().getPluginManager().getPlugin(plugin);
return pl == null ? null : pl.getDescription().getVersion();
}
@@ -1,45 +1,60 @@
package me.neznamy.tab.platforms.bungeecord;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import io.netty.channel.Channel;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import net.md_5.bungee.UserConnection;
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.Protocol;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* TabPlayer for BungeeCord
* TabPlayer implementation for BungeeCord
*/
public class BungeeTabPlayer extends ProxyTabPlayer {
//bungee internals to get player channel
/** Inaccessible bungee internals */
private static Method InitialHandler_getLoginProfile;
private static Method ChannelWrapper_getHandle;
private static Method LoginResult_Property_getValue;
private static Method LoginResult_Property_getSignature;
private static Method LoginResult_getProperties;
private static Method UserConnection_getGamemode;
private static Field wrapperField;
private static Object directionData;
private static Method getId;
static {
try {
Class<?> initialHandler = Class.forName("net.md_5.bungee.connection.InitialHandler");
InitialHandler_getLoginProfile = initialHandler.getMethod("getLoginProfile");
Class<?> channelWrapper = Class.forName("net.md_5.bungee.netty.ChannelWrapper");
ChannelWrapper_getHandle = channelWrapper.getMethod("getHandle");
Class<?> loginResult = Class.forName("net.md_5.bungee.connection.LoginResult");
Class<?> loginResult_Property = Class.forName("net.md_5.bungee.connection.LoginResult$Property");
LoginResult_Property_getValue = loginResult_Property.getMethod("getValue");
LoginResult_Property_getSignature = loginResult_Property.getMethod("getSignature");
LoginResult_getProperties = loginResult.getMethod("getProperties");
Class<?> userConnection = Class.forName("net.md_5.bungee.UserConnection");
UserConnection_getGamemode = userConnection.getMethod("getGamemode");
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 = initialHandler.getDeclaredField("ch");
wrapperField.setAccessible(true);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to initialize fields for packet analysis", e);
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
}
}
@@ -52,8 +67,8 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
public BungeeTabPlayer(ProxiedPlayer p) {
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1);
try {
channel = ((ChannelWrapper) wrapperField.get(getPlayer().getPendingConnection())).getHandle();
} catch (IllegalAccessException e) {
channel = (Channel) ChannelWrapper_getHandle.invoke(wrapperField.get(getPlayer().getPendingConnection()));
} catch (IllegalAccessException | InvocationTargetException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + getPlayer().getName(), e);
}
}
@@ -81,9 +96,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public Skin getSkin() {
LoginResult loginResult = ((InitialHandler)getPlayer().getPendingConnection()).getLoginProfile();
if (loginResult == null || loginResult.getProperties() == null || loginResult.getProperties().length == 0) return null;
return new Skin(loginResult.getProperties()[0].getValue(), loginResult.getProperties()[0].getSignature());
try {
Object loginResult = InitialHandler_getLoginProfile.invoke(getPlayer().getPendingConnection());
if (loginResult == null) return null;
Object[] properties = (Object[]) LoginResult_getProperties.invoke(loginResult);
if (properties == null || properties.length == 0) return null;
return new Skin((String) LoginResult_Property_getValue.invoke(properties[0]),
(String) LoginResult_Property_getSignature.invoke(properties[0]));
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
return null;
}
}
@Override
@@ -116,9 +139,9 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public boolean isVanished() {
try {
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion("PremiumVanish") +
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.PREMIUM_VANISH) +
" generated an error when retrieving vanish status of " + getName(), e);
}
return super.isVanished();
@@ -131,7 +154,12 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public int getGamemode() {
return ((UserConnection)player).getGamemode();
try {
return (int) UserConnection_getGamemode.invoke(player);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get gamemode of " + getPlayer().getName(), e);
return 0;
}
}
@Override
@@ -35,8 +35,8 @@ public class Main extends Plugin {
getProxy().getPluginManager().registerCommand(this, new BTABCommand());
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 10535);
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("global_playerlist_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.GLOBAL_PLAYER_LIST_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
}
/**
@@ -59,9 +59,9 @@ public class Main extends Plugin {
}
/**
* TAB command for bungeecord
* TAB command for BungeeCord
*/
public static class BTABCommand extends Command implements TabExecutor {
private static class BTABCommand extends Command implements TabExecutor {
/**
* Constructs new instance
@@ -1,14 +0,0 @@
package me.neznamy.tab.platforms.bungeecord.event;
import net.md_5.bungee.api.plugin.Event;
/**
* Bungee event that is called each time TAB fully loads. This includes server startup, reload and
* /btab reload
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
}
@@ -1,23 +0,0 @@
package me.neznamy.tab.platforms.bungeecord.event;
import me.neznamy.tab.api.TabPlayer;
import net.md_5.bungee.api.plugin.Event;
/**
* BungeeCord event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private final TabPlayer player;
public TabPlayerLoadEvent(TabPlayer player) {
this.player = player;
}
public TabPlayer getPlayer() {
return player;
}
}
@@ -154,7 +154,7 @@ scoreboard:
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
@@ -200,10 +200,10 @@ placeholder-output-replacements:
"no": ""
"%essentials_nickname%":
"%essentials_nickname%": "%player%" # placeholder did not parse correctly, show real name instead
# bukkit-only placeholders used in default groups.yml, doing this as a fix
"%afk%":
"%afk%": ""
"%afk%": ""
"%vault-prefix%":
"%vault-prefix%" : ""
"%vault-suffix%":
+2 -2
View File
@@ -52,8 +52,8 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
event = { group = "net.kyori", name = "event-method", version.ref = "event" }
# Platforms
bukkit = { group = "com.destroystokyo.paper", name = "paper-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-proxy", version.ref = "bungeecord" }
bukkit = { group = "org.purpurmc.purpur", name = "purpur-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" }
krypton-api = { group = "org.kryptonmc", name = "krypton-api", version.ref = "krypton" }
krypton-server = { group = "org.kryptonmc", name = "krypton-server", version.ref = "krypton" }
krypton-annotationProcessor = { group = "org.kryptonmc", name = "krypton-annotation-processor", version.ref = "krypton" }
+1 -1
View File
@@ -8,7 +8,7 @@ val platforms = setOf(
rootProject.projects.bukkit,
rootProject.projects.bungeecord,
// rootProject.projects.krypton,
rootProject.projects.velocity
// rootProject.projects.velocity
).map { it.dependencyProject }
tasks {
+10 -16
View File
@@ -3,21 +3,15 @@ enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
repositories {
mavenCentral()
maven("https://jitpack.io")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
maven("https://repo.viaversion.com/")
maven("https://repo.aikar.co/content/groups/aikar/")
maven("https://repo.md-5.net/content/groups/public/")
maven("https://ci.lucko.me/plugin/repository/everything/")
maven("https://repo.essentialsx.net/releases/")
maven("https://repo.codemc.org/repository/maven-public")
maven("https://repo.kryptonmc.org/releases")
maven("https://repo.kryptonmc.org/snapshots")
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://nexus.velocitypowered.com/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://papermc.io/repo/repository/maven-public/")
mavenCentral() // Netty, SnakeYaml, json-simple, Gson, slf4j, Guava, Kyori event, bStats
maven("https://jitpack.io") // PremiumVanish
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // PlaceholderAPI
maven("https://repo.viaversion.com/") // ViaVersion
maven("https://repo.essentialsx.net/releases/") // Essentials
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee, BungeeCord
maven("https://repo.kryptonmc.org/releases") // YamlAssist, LuckPerms
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.purpurmc.org/snapshots") // Purpur, LibsDisguises, AuthLib, Vault
}
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
@@ -31,7 +25,7 @@ rootProject.name = "TAB"
include(":api")
include(":shared")
//include(":krypton")
include(":velocity")
//include(":velocity")
include(":bukkit")
include(":bungeecord")
include(":jar")
@@ -18,7 +18,7 @@ import me.neznamy.tab.api.task.ThreadManager;
public class CpuManager implements ThreadManager {
/** Data reset interval in milliseconds */
private static final int BUFFER_SIZE_MILLIS = 10000;
private final int BUFFER_SIZE_MILLIS = 10000;
/** Active time in current time period saved as nanoseconds from features */
private Map<String, Map<String, AtomicLong>> featureUsageCurrent = new ConcurrentHashMap<>();
@@ -386,21 +386,7 @@ public class CpuManager implements ThreadManager {
}
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, Runnable task) {
try {
return threadPool.submit(() -> {
try {
Thread.sleep(delayMilliseconds);
submit(task);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
}
});
} catch (OutOfMemoryError e) {
TAB.getInstance().getErrorManager().criticalError("Failed to create new delayed task, active thread count: " +
threadPool.getActiveCount() + " / " + threadPool.getPoolSize(), e);
return null;
}
public ThreadPoolExecutor getThreadPool() {
return threadPool;
}
}
@@ -110,7 +110,7 @@ public class DynamicText implements Property {
rawFormattedValue0 = sb.toString();
}
}
rawFormattedValue0 = RGBUtils.getInstance().applyFormats(rawFormattedValue0, true);
rawFormattedValue0 = RGBUtils.getInstance().applyCleanGradients(rawFormattedValue0);
rawFormattedValue = EnumChatFormat.color(rawFormattedValue0);
placeholders = placeholders0.toArray(new String[0]);
relPlaceholders = relPlaceholders0.toArray(new String[0]);
@@ -108,13 +108,18 @@ public class ErrorManager {
private synchronized void printError(String message, List<String> error, boolean intoConsoleToo, File file) {
try {
if (!file.exists()) Files.createFile(file.toPath());
if (file.length() > 1000000) return; //not going over 1 MB
try (BufferedWriter buf = new BufferedWriter(new FileWriter(file, true))){
if (message != null) {
write(buf, "&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ", EnumChatFormat.decolor(message), intoConsoleToo);
if (file.length() < 1000000)
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText("&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ").toRawText() + EnumChatFormat.decolor(message) + System.getProperty("line.separator"));
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().sendConsoleMessage(EnumChatFormat.color("&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ") + EnumChatFormat.decolor(message), false);
}
for (String line : error) {
write(buf, "&c", line, intoConsoleToo);
if (file.length() < 1000000)
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText("&c").toRawText() + line + System.getProperty("line.separator"));
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().sendConsoleMessage(EnumChatFormat.color("&c") + line, false);
}
}
} catch (IOException ex) {
@@ -154,23 +159,6 @@ public class ErrorManager {
printError(message, t, false, placeholderErrorLog);
}
/**
* Writes message into buffer and console if set
*
* @param buf
* writer to write message to
* @param message
* message to write
* @param forceConsole
* whether to send into console even without debug mode
* @throws IOException
* if I/O writer operation fails
*/
private void write(BufferedWriter buf, String prefix, String message, boolean forceConsole) throws IOException {
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText(prefix).toRawText() + message + System.getProperty("line.separator"));
if (TAB.getInstance().getConfiguration().isDebugMode() || forceConsole) TAB.getInstance().sendConsoleMessage(EnumChatFormat.color(prefix) + message, false);
}
/**
* Prints error message and stack trace into errors.log file as well as the console
*
@@ -124,11 +124,15 @@ public class FeatureManagerImpl implements FeatureManager {
long millis = System.currentTimeMillis();
TAB.getInstance().addPlayer(connectedPlayer);
for (TabFeature f : values) {
if (!f.overridesMethod("onJoin")) continue;
long time = System.nanoTime();
f.onJoin(connectedPlayer);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
try {
if (!f.overridesMethod("onJoin")) continue;
long time = System.nanoTime();
f.onJoin(connectedPlayer);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
} catch (Throwable e) {
TAB.getInstance().getErrorManager().criticalError("Feature " + f.getClass().getSimpleName() + " failed to process player " + connectedPlayer.getName(), e);
}
}
((ITabPlayer)connectedPlayer).markAsLoaded(true);
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
@@ -157,7 +161,7 @@ public class FeatureManagerImpl implements FeatureManager {
f.onWorldChange(changed, from, to);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%world%")).updateValue(changed, to);
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.WORLD)).updateValue(changed, to);
}
/**
@@ -181,7 +185,7 @@ public class FeatureManagerImpl implements FeatureManager {
f.onServerChange(changed, from, to);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.SERVER_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%server%")).updateValue(changed, to);
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.SERVER)).updateValue(changed, to);
}
/**
@@ -58,21 +58,22 @@ public class GroupManager extends TabFeature {
*/
private String getByPrimary(TabPlayer player) {
try {
return plugin.getPrimaryGroup(player);
String group = plugin.getPrimaryGroup(player);
return group == null ? TabConstants.NO_GROUP : group;
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to get permission group of " + player.getName() + " using " + plugin.getName() + " v" + plugin.getVersion(), e);
return TabConstants.DEFAULT_GROUP;
return TabConstants.NO_GROUP;
}
}
/**
* Returns player's permission group based on highest permission
* or {@link TabConstants#DEFAULT_GROUP} if player has no permission.
* or {@link TabConstants#NO_GROUP} if player has no permission.
*
* @param player
* Player to get permission group of
* @return Highest permission group player has permission for
* or {@link TabConstants#DEFAULT_GROUP} if player does not have any
* or {@link TabConstants#NO_GROUP} if player does not have any
*/
private String getByPermission(TabPlayer player) {
for (String group : primaryGroupFindingList) {
@@ -80,7 +81,7 @@ public class GroupManager extends TabFeature {
return group;
}
}
return TabConstants.DEFAULT_GROUP;
return TabConstants.NO_GROUP;
}
/**
@@ -14,7 +14,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.event.impl.PlayerLoadEventImpl;
import me.neznamy.tab.shared.features.NickCompatibility;
import org.geysermc.floodgate.api.FloodgateApi;
/**
@@ -77,6 +76,9 @@ public abstract class ITabPlayer implements TabPlayer {
/** Scoreboard objectives player has registered */
private final List<String> registeredObjectives = new ArrayList<>();
/** Player's name as seen in GameProfile, can be altered by nick plugins */
private String nickname;
/**
* Constructs new instance with given parameters
*
@@ -97,6 +99,7 @@ public abstract class ITabPlayer implements TabPlayer {
this.player = player;
this.uniqueId = uniqueId;
this.name = name;
this.nickname = name;
this.server = server;
this.world = world;
this.version = ProtocolVersion.fromNetworkId(protocolVersion);
@@ -133,6 +136,11 @@ public abstract class ITabPlayer implements TabPlayer {
}
}
@Override
public boolean setProperty(TabFeature feature, String identifier, String rawValue) {
return setProperty(feature, identifier, rawValue, null, false);
}
/**
* Sets team name to given value
*
@@ -175,7 +183,7 @@ public abstract class ITabPlayer implements TabPlayer {
Preconditions.checkNotNull(permissionGroup, "permissionGroup");
if (this.permissionGroup.equals(permissionGroup)) return;
this.permissionGroup = permissionGroup;
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%group%")).updateValue(this, permissionGroup);
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.GROUP)).updateValue(this, permissionGroup);
forceRefresh();
}
@@ -258,6 +266,7 @@ public abstract class ITabPlayer implements TabPlayer {
@Override
public void forceRefresh() {
if (!onJoinFinished) return;
TAB.getInstance().getFeatureManager().refresh(this, true);
}
@@ -335,18 +344,6 @@ public abstract class ITabPlayer implements TabPlayer {
sendCustomPacket(packet);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature);
}
@Override
public void sendPacket(Object nmsPacket, TabFeature feature) {
sendPacket(nmsPacket);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature.getFeatureName());
}
@Override
public void sendPacket(Object packet, String feature) {
sendPacket(packet);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature);
}
@Override
public Property getProperty(String name) {
@@ -404,14 +401,13 @@ public abstract class ITabPlayer implements TabPlayer {
public boolean isBedrockPlayer() {
return bedrockPlayer;
}
@Override
public boolean setProperty(TabFeature feature, String identifier, String rawValue) {
return setProperty(feature, identifier, rawValue, null, false);
}
@Override
public String getNickname() {
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY)).getNickname(this);
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
@@ -1,8 +1,10 @@
package me.neznamy.tab.shared;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import org.slf4j.Logger;
/**
* An interface with methods that are called in universal code,
@@ -48,6 +50,15 @@ public abstract class Platform {
return packetBuilder;
}
public void sendConsoleMessage(String message, boolean translateColors) {
Object logger = TAB.getInstance().getLogger();
if (logger instanceof java.util.logging.Logger) {
((java.util.logging.Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
} else if (logger instanceof Logger) {
((Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
}
}
/**
* Detects permission plugin and returns it's representing object
*
@@ -69,13 +80,6 @@ public abstract class Platform {
*/
public abstract void registerUnknownPlaceholder(String identifier);
/**
* Returns {@code true} if this platform is a proxy, {@code false} if a game server
*
* @return {@code true} if this platform is a proxy, {@code false} if a game server
*/
public abstract boolean isProxy();
/**
* Performs platform-specific plugin manager call and returns the result.
* If plugin is not installed, returns {@code null}.
@@ -1,22 +1,6 @@
package me.neznamy.tab.shared;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.shared.event.EventBusImpl;
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
import org.slf4j.Logger;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.HeaderFooterManager;
import me.neznamy.tab.api.PropertyConfiguration;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.TablistFormatManager;
import me.neznamy.tab.api.*;
import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
@@ -24,20 +8,20 @@ import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.shared.command.DisabledCommand;
import me.neznamy.tab.shared.command.TabCommand;
import me.neznamy.tab.shared.config.Configs;
import me.neznamy.tab.shared.event.EventBusImpl;
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
import me.neznamy.tab.shared.features.*;
import me.neznamy.tab.shared.features.alignedplayerlist.AlignedPlayerList;
import me.neznamy.tab.shared.features.BelowName;
import me.neznamy.tab.shared.features.GhostPlayerFix;
import me.neznamy.tab.shared.features.HeaderFooter;
import me.neznamy.tab.shared.features.NickCompatibility;
import me.neznamy.tab.shared.features.PingSpoof;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.SpectatorFix;
import me.neznamy.tab.shared.features.YellowNumber;
import me.neznamy.tab.shared.features.layout.LayoutManager;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Main class of the plugin storing data and implementing API
@@ -112,6 +96,9 @@ public class TAB extends TabAPI {
/** Plugin's console logger provided by platform */
private final Object logger;
/** File with YAML syntax error, which prevented plugin from loading */
private String brokenFile;
/**
* Constructs new instance with given parameters and sets this
* new instance as {@link me.neznamy.tab.api.TabAPI} instance.
@@ -162,11 +149,10 @@ public class TAB extends TabAPI {
public String load() {
try {
long time = System.currentTimeMillis();
this.errorManager = new ErrorManager();
errorManager = new ErrorManager();
cpu = new CpuManager();
featureManager = new FeatureManagerImpl();
configuration = new Configs(this);
configuration.loadFiles();
configuration = new Configs();
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, new PlaceholderManagerImpl());
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, new GroupManager(platform.detectPermissionPlugin()));
platform.loadFeatures();
@@ -182,7 +168,8 @@ public class TAB extends TabAPI {
} catch (YAMLException e) {
sendConsoleMessage("&cDid not enable due to a broken configuration file.", true);
kill();
return configuration.getReloadFailedMessage();
return (configuration == null ? "&4Failed to reload, file %file% has broken syntax. Check console for more info."
: configuration.getMessages().getReloadFailBrokenFile()).replace("%file%", brokenFile);
} catch (Exception e) {
errorManager.criticalError("Failed to enable. Did you just invent a new way to break the plugin by misconfiguring it?", e);
kill();
@@ -395,6 +382,15 @@ public class TAB extends TabAPI {
return dataFolder;
}
/**
* Returns {@link #logger}
*
* @return {@link #logger}
*/
public Object getLogger() {
return logger;
}
@Override
public EventBusImpl getEventBus() {
return eventBus;
@@ -441,11 +437,7 @@ public class TAB extends TabAPI {
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
if (logger instanceof java.util.logging.Logger) {
((java.util.logging.Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
} else if (logger instanceof Logger) {
((Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
}
platform.sendConsoleMessage(message, translateColors);
}
@Override
@@ -485,7 +477,7 @@ public class TAB extends TabAPI {
@Override
public void setBrokenFile(String file) {
configuration.setBrokenFile(file);
this.brokenFile = file;
}
@Override
@@ -7,7 +7,8 @@ package me.neznamy.tab.shared;
public class TabConstants {
public static final String PLUGIN_VERSION = "@plugin_version@";
public static final String DEFAULT_GROUP = "NONE";
public static final String NO_GROUP = "NONE";
public static final String DEFAULT_GROUP = "_DEFAULT_";
public static final String PLUGIN_MESSAGE_CHANNEL_NAME = "tab:bridge-2";
public static final String REDIS_CHANNEL_NAME = "TAB";
@@ -49,7 +50,6 @@ public class TabConstants {
public static final String PLACEHOLDER_REFRESHING = "Refreshing placeholders";
public static final String GROUP_REFRESHING = "Refreshing groups";
public static final String V1_8_0_BUG_COMPENSATION = "Compensating for 1.8.0 bugs";
public static final String REFRESHING_NAME_TAG_VISIBILITY = "Refreshing NameTag visibility";
public static final String SCOREBOARD_PACKET_CHECK = "Checking for other plugins";
public static final String PROCESSING_PLAYER_MOVEMENT = "Processing player movement";
@@ -260,6 +260,10 @@ public class TabConstants {
return "scoreboard-" + line;
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
}
public static String layout(String name) {
return "layout-" + name;
}
@@ -279,9 +283,87 @@ public class TabConstants {
public static String bossBarColorStyle(String name) {
return "BossBar-color-style-" + name;
}
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
/**
* All internal placeholders offered by the plugin
*/
public static class Placeholder {
// Universal
public static final String DISPLAY_NAME = "%displayname%";
public static final String GROUP = "%group%";
public static final String VANISHED = "%vanished%";
public static final String WORLD = "%world%";
public static final String WORLD_ONLINE = "%worldonline%";
public static final String SERVER = "%server%";
public static final String SERVER_ONLINE = "%serveronline%";
public static final String PLAYER = "%player%";
public static final String TIME = "%time%";
public static final String DATE = "%date%";
public static final String PING = "%ping%";
public static final String PLAYER_VERSION = "%player-version%";
public static final String PLAYER_VERSION_ID = "%player-version-id%";
public static final String MEMORY_USED = "%memory-used%";
public static final String MEMORY_MAX = "%memory-max%";
public static final String MEMORY_USED_GB = "%memory-used-gb%";
public static final String MEMORY_MAX_GB = "%memory-max-gb%";
public static final String ONLINE = "%online%";
public static final String STAFF_ONLINE = "%staffonline%";
public static final String NON_STAFF_ONLINE = "%nonstaffonline%";
public static final String LUCKPERMS_PREFIX = "%luckperms-prefix%";
public static final String LUCKPERMS_SUFFIX = "%luckperms-suffix%";
public static final String COUNTDOWN = "%countdown%";
// Placeholders for internal use
public static final String COLLISION = "%collision%";
public static final String INVISIBLE = "%invisible%";
public static final String VEHICLE = "%vehicle%";
// Bukkit only
public static final String TPS = "%tps%";
public static final String MSPT = "%mspt%";
public static final String AFK = "%afk%";
public static final String ESSENTIALS_NICK = "%essentialsnick%";
public static final String VAULT_PREFIX = "%vault-prefix%";
public static final String VAULT_SUFFIX = "%vault-suffix%";
public static final String HEALTH = "%health%";
public static String condition(String name) {
return "%condition:" + name + "%";
}
public static String animation(String name) {
return "%animation:" + name + "%";
}
}
/**
* All plugins TAB hooks into
*/
public static class Plugin {
public static final String LUCKPERMS = "LuckPerms";
public static final String VAULT = "Vault";
public static final String PLACEHOLDER_API = "PlaceholderAPI";
public static final String LIBS_DISGUISES = "LibsDisguises";
public static final String VIAVERSION = "ViaVersion";
public static final String PROTOCOL_SUPPORT = "ProtocolSupport";
public static final String ESSENTIALS = "Essentials";
public static final String REDIS_BUNGEE = "RedisBungee";
public static final String PREMIUM_VANISH = "PremiumVanish";
}
/**
* TAB's custom metrics charts
*/
public static class MetricsChart {
public static final String PERMISSION_SYSTEM = "permission_system";
public static final String GLOBAL_PLAYER_LIST_ENABLED = "global_playerlist_enabled";
public static final String PLACEHOLDER_API = "placeholderapi";
public static final String SERVER_VERSION = "server_version";
public static final String UNLIMITED_NAME_TAG_MODE_ENABLED = "unlimited_nametag_mode_enabled";
}
}
@@ -20,9 +20,7 @@ import me.neznamy.tab.shared.TabConstants;
public class CpuCommand extends SubCommand {
private final DecimalFormat decimal3 = new DecimalFormat("#.###");
private static final char LINE_CHAR = (char)9553;
private static final String SEPARATOR = "&8&l" + LINE_CHAR + "&8&m ";
private final char LINE_CHAR = (char)9553;
/**
* Constructs new instance
@@ -33,6 +31,7 @@ public class CpuCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
String SEPARATOR = "&8&l" + LINE_CHAR + "&8&m ";
TAB tab = TAB.getInstance();
Map<String, Float> placeholders = tab.getCPUManager().getPlaceholderUsage();
double placeholdersTotal = placeholders.values().stream().mapToDouble(Float::floatValue).sum();
@@ -77,7 +76,7 @@ public class CpuCommand extends SubCommand {
if (printCounter++ == 5) break;
String refresh = "";
Placeholder p = TAB.getInstance().getPlaceholderManager().getPlaceholder(entry.getKey());
if (p != null && !p.isTriggerMode()) refresh = " &8(" + p.getRefresh() + ")&7";
if (p != null && p.getRefresh() != -1) refresh = " &8(" + p.getRefresh() + ")&7";
String colorized = entry.getKey().startsWith("%sync:") ? "&c" + decimal3.format(entry.getValue()) : colorize(decimal3.format(entry.getValue()), 1, 0.3f);
sendMessage(sender, String.format("&8&l%s &7%s - %s%%", LINE_CHAR, entry.getKey() + refresh, colorized));
}
@@ -126,7 +126,7 @@ public class DebugCommand extends SubCommand {
*/
private String getGroup(TabPlayer analyzed) {
if (TAB.getInstance().getGroupManager().isGroupsByPermissions()) {
return "&eHighest permission for group: &a" + analyzed.getGroup();
return "&eHighest group permission: &8tab.group.&a" + analyzed.getGroup();
}
return "&ePrimary permission group: &a" + analyzed.getGroup();
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.command;
import java.util.ArrayList;
import java.util.List;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
/**
@@ -32,7 +33,7 @@ public class DisabledCommand {
}
} else {
if (hasAdminPermission) {
String command = !TAB.getInstance().getPlatform().isProxy() ? "/tab" : "/btab";
String command = TAB.getInstance().getServerVersion() != ProtocolVersion.PROXY ? "/tab" : "/btab";
messages.add("&m ");
messages.add(" &cPlugin is disabled due to an error. Check console for more details.");
messages.add(" &8>> &3&l" + command + " reload");
@@ -54,7 +54,7 @@ public class GroupCommand extends PropertyCommand {
if (hasPermission(sender, TabConstants.Permission.COMMAND_DATA_REMOVE)) {
TAB.getInstance().getConfiguration().getGroups().remove(group);
for (TabPlayer pl : TAB.getInstance().getOnlinePlayers()) {
if (pl.getGroup().equals(group) || "_DEFAULT_".equals(group)){
if (pl.getGroup().equals(group) || TabConstants.DEFAULT_GROUP.equals(group)){
pl.forceRefresh();
}
}
@@ -123,7 +123,7 @@ public class GroupCommand extends PropertyCommand {
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getGroups().setProperty(group, type, server, world, value.length() == 0 ? null : value);
for (TabPlayer pl : TAB.getInstance().getOnlinePlayers()) {
if (pl.getGroup().equals(group) || "_DEFAULT_".equals(group)){
if (pl.getGroup().equals(group) || TabConstants.DEFAULT_GROUP.equals(group)){
pl.forceRefresh();
}
}
@@ -133,7 +133,7 @@ public class GroupCommand extends PropertyCommand {
public List<String> complete(TabPlayer sender, String[] arguments) {
if (arguments.length == 1) {
Set<String> groups = new HashSet<>(TAB.getInstance().getConfiguration().getGroups().getAllEntries());
groups.add("_DEFAULT_");
groups.add(TabConstants.DEFAULT_GROUP);
return groups.stream().filter(group -> group.toLowerCase().startsWith(arguments[0].toLowerCase())).collect(Collectors.toList());
}
return super.complete(sender, arguments);
@@ -6,6 +6,7 @@ import java.util.List;
import com.google.common.collect.Lists;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
@@ -80,7 +81,7 @@ public class TabCommand extends SubCommand {
TAB.getInstance().sendConsoleMessage("&3TAB v" + TabConstants.PLUGIN_VERSION, true);
}
for (String message : getMessages().getHelpMenu()) {
if (TAB.getInstance().getPlatform().isProxy()) message = message.replace("/tab", "/btab");
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) message = message.replace("/tab", "/btab");
sendMessage(sender, message);
}
}
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.config;
import me.neznamy.tab.api.PropertyConfiguration;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.config.YamlConfigurationFile;
import me.neznamy.tab.shared.TAB;
@@ -19,33 +20,27 @@ import java.util.List;
*/
public class Configs {
private final TAB tab;
private final Converter converter = new Converter();
//config.yml file
private ConfigurationFile config;
private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream(TAB.getInstance().getPlatform().getConfigName()),
new File(TAB.getInstance().getDataFolder(), "config.yml"));
private boolean bukkitPermissions;
//hidden config options
private boolean unregisterBeforeRegister;
private boolean removeGhostPlayers;
private boolean pipelineInjection;
private final boolean bukkitPermissions = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY && config.getBoolean("use-bukkit-permissions-manager", false);
private final boolean debugMode = config.getBoolean("debug", false);
private final boolean removeGhostPlayers = getSecretOption("remove-ghost-players", false);
private final boolean pipelineInjection = getSecretOption("pipeline-injection", true) && TAB.getInstance().getServerVersion().getMinorVersion() >= 8;
//animations.yml file
private ConfigurationFile animation;
private final ConfigurationFile animation = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("animations.yml"),
new File(TAB.getInstance().getDataFolder(), "animations.yml"));
//messages.yml file
private MessageFile messages;
//default reload message in case plugin did not load translation file due to an error
private String reloadFailed = "&4Failed to reload, file %file% has broken syntax. Check console for more info.";
private final MessageFile messages = new MessageFile();
//playerdata.yml, used for bossbar & scoreboard toggle saving
private ConfigurationFile playerdata;
private ConfigurationFile layout;
private final ConfigurationFile layout = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("layout.yml"),
new File(TAB.getInstance().getDataFolder(), "layout.yml"));
private PropertyConfiguration groupFile;
@@ -53,58 +48,20 @@ public class Configs {
private MySQL mysql;
private boolean debugMode;
private String brokenFile;
/**
* Constructs new instance with given parameter
*
* @param tab
* tab instance
*/
public Configs(TAB tab) {
this.tab = tab;
}
/**
* Loads all configuration files and converts files to latest version
* Constructs new instance and loads configuration files.
* If needed, converts old configuration files as well.
*
* @throws IOException
* if File I/O operation fails
* @throws YAMLException
* if files contain syntax errors
*/
public void loadFiles() throws YAMLException, IOException {
messages = new MessageFile();
ClassLoader loader = Configs.class.getClassLoader();
loadConfig();
animation = new YamlConfigurationFile(loader.getResourceAsStream("animations.yml"), new File(tab.getDataFolder(), "animations.yml"));
converter.convertAnimationFile(animation);
layout = new YamlConfigurationFile(loader.getResourceAsStream("layout.yml"), new File(tab.getDataFolder(), "layout.yml"));
reloadFailed = messages.getReloadFailBrokenFile();
}
/**
* Loads config.yml and some of its values
*
* @throws IOException
* if File I/O operation fails
* @throws YAMLException
* if files contain syntax errors
*/
public void loadConfig() throws YAMLException, IOException {
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(tab.getPlatform().getConfigName()), new File(tab.getDataFolder(), "config.yml"));
public Configs() throws YAMLException, IOException {
Converter converter = new Converter();
converter.convertToV3(config);
converter.removeOldOptions(config);
debugMode = getConfig().getBoolean("debug", false);
if (tab.getPlatform().isProxy()) {
bukkitPermissions = getConfig().getBoolean("use-bukkit-permissions-manager", false);
} else {
unregisterBeforeRegister = getSecretOption("unregister-before-register", true);
}
removeGhostPlayers = getSecretOption("remove-ghost-players", false);
pipelineInjection = getSecretOption("pipeline-injection", true) && tab.getServerVersion().getMinorVersion() >= 8;
converter.convertAnimationFile(animation);
if (config.getBoolean("mysql.enabled", false)) {
try {
mysql = new MySQL(config.getString("mysql.host", "127.0.0.1"), config.getInt("mysql.port", 3306),
@@ -116,8 +73,8 @@ public class Configs {
TAB.getInstance().getErrorManager().criticalError("Failed to connect to MySQL", e);
}
}
groupFile = new YamlPropertyConfigurationFile(Configs.class.getClassLoader().getResourceAsStream("groups.yml"), new File(tab.getDataFolder(), "groups.yml"));
userFile = new YamlPropertyConfigurationFile(Configs.class.getClassLoader().getResourceAsStream("users.yml"), new File(tab.getDataFolder(), "users.yml"));
groupFile = new YamlPropertyConfigurationFile(getClass().getClassLoader().getResourceAsStream("groups.yml"), new File(TAB.getInstance().getDataFolder(), "groups.yml"));
userFile = new YamlPropertyConfigurationFile(getClass().getClassLoader().getResourceAsStream("users.yml"), new File(TAB.getInstance().getDataFolder(), "users.yml"));
}
/**
@@ -131,14 +88,10 @@ public class Configs {
*/
@SuppressWarnings("unchecked")
public <T> T getSecretOption(String path, T defaultValue) {
Object value = getConfig().getObject(path);
Object value = config.getObject(path);
return value == null ? defaultValue : (T) value;
}
public boolean isUnregisterBeforeRegister() {
return unregisterBeforeRegister;
}
public MessageFile getMessages() {
return messages;
}
@@ -167,20 +120,15 @@ public class Configs {
return pipelineInjection;
}
public String getReloadFailedMessage() {
String message = messages == null ? reloadFailed : messages.getReloadFailBrokenFile();
return message.replace("%file%", brokenFile);
}
public ConfigurationFile getPlayerDataFile() {
if (playerdata == null) {
File file = new File(tab.getDataFolder(), "playerdata.yml");
File file = new File(TAB.getInstance().getDataFolder(), "playerdata.yml");
try {
if (file.exists() || file.createNewFile()) {
playerdata = new YamlConfigurationFile(null, file);
}
} catch (IOException e) {
tab.getErrorManager().criticalError("Failed to load playerdata.yml", e);
TAB.getInstance().getErrorManager().criticalError("Failed to load playerdata.yml", e);
}
}
return playerdata;
@@ -215,8 +163,4 @@ public class Configs {
public boolean isDebugMode() {
return debugMode;
}
public void setBrokenFile(String file) {
this.brokenFile = file;
}
}
@@ -1,8 +1,10 @@
package me.neznamy.tab.shared.config;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.config.YamlConfigurationFile;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import java.io.File;
import java.io.IOException;
@@ -112,7 +114,7 @@ public class Converter {
newConfig.set("scoreboard-teams.anti-override", oldConfig.getBoolean("anti-override.scoreboard-teams", true));
newConfig.set("scoreboard-teams.enable-collision", oldConfig.getBoolean("enable-collision", true));
newConfig.set("scoreboard-teams.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.nametag", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy()) {
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
newConfig.set("scoreboard-teams.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.nametag", Collections.singletonList("disabledserver")));
} else {
newConfig.set("scoreboard-teams.unlimited-nametag-mode.enabled", oldConfig.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false));
@@ -157,7 +159,7 @@ public class Converter {
sortingTypes.add(type + ":" + sortingPlaceholder);
}
}
sortingTypes.add("PLACEHOLDER_A_TO_Z:%player%");
sortingTypes.add("PLACEHOLDER_A_TO_Z:" + TabConstants.Placeholder.PLAYER);
newConfig.set("scoreboard-teams.sorting-types", sortingTypes);
}
@@ -165,7 +167,7 @@ public class Converter {
newConfig.set("tablist-name-formatting.enabled", oldConfig.getBoolean("change-tablist-prefix-suffix", true));
newConfig.set("tablist-name-formatting.anti-override", oldConfig.getBoolean("anti-override.tablist-names", true));
newConfig.set("tablist-name-formatting.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.tablist-names", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY)
newConfig.set("tablist-name-formatting.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.tablist-names", Collections.singletonList("disabledserver")));
if (premiumConfig != null) {
newConfig.set("tablist-name-formatting.align-tabsuffix-on-the-right", premiumConfig.getBoolean("align-tabsuffix-on-the-right", false));
@@ -177,17 +179,17 @@ public class Converter {
}
private void convertYellowNumber(ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("yellow-number-in-tablist.enabled", !oldConfig.getString("yellow-number-in-tablist","%ping%").equals(""));
newConfig.set("yellow-number-in-tablist.value", oldConfig.getString("yellow-number-in-tablist","%ping%"));
newConfig.set("yellow-number-in-tablist.enabled", !oldConfig.getString("yellow-number-in-tablist", TabConstants.Placeholder.PING).equals(""));
newConfig.set("yellow-number-in-tablist.value", oldConfig.getString("yellow-number-in-tablist", TabConstants.Placeholder.PING));
newConfig.set("yellow-number-in-tablist.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.yellow-number", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY)
newConfig.set("yellow-number-in-tablist.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.yellow-number", Collections.singletonList("disabledserver")));
}
private void convertBelowName(ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("belowname-objective", oldConfig.getConfigurationSection("classic-vanilla-belowname"));
newConfig.set("belowname-objective.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.belowname", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY)
newConfig.set("belowname-objective.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.belowname", Collections.singletonList("disabledserver")));
}
@@ -197,12 +199,12 @@ public class Converter {
newConfig.set("bossbar.remember-toggle-choice", bossBar.getBoolean("remember-toggle-choice", false));
newConfig.set("bossbar.hidden-by-default", bossBar.getBoolean("hidden-by-default", false));
newConfig.set("bossbar.disable-in-worlds", bossBar.getObject("disable-features-in-worlds.bossbar"));
if (TAB.getInstance().getPlatform().isProxy())
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY)
newConfig.set("bossbar.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.bossbar", Collections.singletonList("disabledserver")));
Map<Object, Map<String, Object>> bars = bossBar.getConfigurationSection("bars");
Map<String, List<Object>> perWorldBossBars = bossBar.getConfigurationSection("per-world");
List<Object> activeBossBars = new ArrayList<>(bossBar.getStringList("default-bars", new ArrayList<>()));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
String separator = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ? "server" : "world";
if (perWorldBossBars != null) {
for (Map.Entry<String, List<Object>> entry : perWorldBossBars.entrySet()) {
for (Object bar : entry.getValue()) {
@@ -224,7 +226,7 @@ public class Converter {
}
private void convertScoreboard(ConfigurationFile newConfig, ConfigurationFile premiumConfig) {
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
String separator = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ? "server" : "world";
newConfig.set("scoreboard", premiumConfig.getObject("scoreboard"));
newConfig.set("scoreboard.permission-required-to-toggle", null);
Map<String, Map<String,Object>> scoreboards = premiumConfig.getConfigurationSection("scoreboards");
@@ -261,7 +263,7 @@ public class Converter {
newConfig.set("scoreboard.delay-on-join-milliseconds", 0);
newConfig.set("scoreboard.respect-other-plugins", true);
newConfig.set("scoreboard.disable-in-worlds", Collections.singletonList("disabledworld"));
if (TAB.getInstance().getPlatform().isProxy()) {
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
newConfig.set("scoreboard.disable-in-servers", Collections.singletonList("disabledserver"));
}
newConfig.set("scoreboard.scoreboards.admin.display-condition", "permission:tab.scoreboard.admin");
@@ -280,10 +282,10 @@ public class Converter {
newConfig.set("header-footer.header", oldConfig.getStringList("header"));
newConfig.set("header-footer.footer", oldConfig.getStringList("footer"));
newConfig.set("header-footer.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.header-footer", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY)
newConfig.set("header-footer.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.header-footer", Collections.singletonList("disabledserver")));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
String separator = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ? "server" : "world";
Map<String, Map<String, Object>> perWorldSettings = oldConfig.getConfigurationSection("per-" + separator + "-settings");
Map<String, Object> headerFooterMap = new LinkedHashMap<>();
for (Map.Entry<String, Map<String, Object>> worldEntry : new LinkedHashMap<>(perWorldSettings).entrySet()) {
@@ -312,7 +314,7 @@ public class Converter {
newConfig.set("conditions.nick.yes", "%player%");
newConfig.set("conditions.nick.no", "~%essentials_nickname%");
}
if (!TAB.getInstance().getPlatform().isProxy()) {
if (TAB.getInstance().getServerVersion() != ProtocolVersion.PROXY) {
newConfig.set("placeholder-output-replacements.%afk%.true", placeholders.remove("afk-yes"));
newConfig.set("placeholder-output-replacements.%afk%.false", placeholders.remove("afk-no"));
}
@@ -331,7 +333,7 @@ public class Converter {
newConfig.set("mysql.username", "user");
newConfig.set("mysql.password", "password");
if (TAB.getInstance().getPlatform().isProxy()) {
if (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
newConfig.set("global-playerlist", oldConfig.getConfigurationSection("global-playerlist"));
newConfig.set("global-playerlist.update-latency", false);
newConfig.set("use-bukkit-permissions-manager", false);
@@ -345,7 +347,7 @@ public class Converter {
groups.setValues(oldConfig.getConfigurationSection("Groups"));
users.setValues(oldConfig.getConfigurationSection("Users"));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
String separator = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ? "server" : "world";
Map<String,Map<String,Object>> perWorldSettings = oldConfig.getConfigurationSection("per-" + separator + "-settings");
Map<String,Object> groupMap = new LinkedHashMap<>();
Map<String,Object> userMap = new LinkedHashMap<>();
@@ -359,13 +361,13 @@ public class Converter {
}
}
groups.set("per-" + separator, groupMap);
groups.set("_DEFAULT_", groups.getConfigurationSection("_OTHER_"));
groups.set(TabConstants.DEFAULT_GROUP, groups.getConfigurationSection("_OTHER_"));
groups.set("_OTHER_", null);
users.set("per-" + separator, userMap);
for (Object world : groups.getConfigurationSection("per-" + separator).keySet()) {
String gPath = "per-" + separator + "." + world;
if (!groups.hasConfigOption(gPath + "._OTHER_")) continue;
groups.set(gPath + "._DEFAULT_", groups.getObject(gPath + "._OTHER_"));
groups.set(gPath + "." + TabConstants.DEFAULT_GROUP, groups.getObject(gPath + "._OTHER_"));
groups.set(gPath + "._OTHER_", null);
}
}
@@ -5,6 +5,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import me.neznamy.tab.shared.TabConstants;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.PropertyConfiguration;
@@ -13,9 +14,8 @@ import me.neznamy.tab.shared.TAB;
public class YamlPropertyConfigurationFile extends YamlConfigurationFile implements PropertyConfiguration {
private static final String PER_SERVER = "per-server";
private static final String PER_WORLD = "per-world";
private static final String DEFAULT_GROUP = "_DEFAULT_";
private final String PER_SERVER = "per-server";
private final String PER_WORLD = "per-world";
private final String category;
private final List<Object> worldGroups = new ArrayList<>(getConfigurationSection(PER_WORLD).keySet());
@@ -43,20 +43,20 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
if ((value = getObject(new String[] {PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, world), name, property})) != null) {
return new String[] {toString(value), category + "=" + name + ", world=" + world};
}
if ((value = getObject(new String[] {PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, world), DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + DEFAULT_GROUP + ", world=" + world};
if ((value = getObject(new String[] {PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, world), TabConstants.DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP + ", world=" + world};
}
if ((value = getObject(new String[] {PER_SERVER, TAB.getInstance().getConfiguration().getGroup(serverGroups, server), name, property})) != null) {
return new String[] {toString(value), category + "=" + name + ", server=" + server};
}
if ((value = getObject(new String[] {PER_SERVER, TAB.getInstance().getConfiguration().getGroup(serverGroups, server), DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + DEFAULT_GROUP + ", server=" + server};
if ((value = getObject(new String[] {PER_SERVER, TAB.getInstance().getConfiguration().getGroup(serverGroups, server), TabConstants.DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP + ", server=" + server};
}
if ((value = getObject(new String[] {name, property})) != null) {
return new String[] {toString(value), category + "=" + name};
}
if ((value = getObject(new String[] {DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + DEFAULT_GROUP};
if ((value = getObject(new String[] {TabConstants.DEFAULT_GROUP, property})) != null) {
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP};
}
return new String[0];
}
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.config.mysql;
import me.neznamy.tab.api.PropertyConfiguration;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.config.MySQL;
import javax.sql.rowset.CachedRowSet;
@@ -10,7 +11,6 @@ import java.util.*;
public class MySQLGroupConfiguration implements PropertyConfiguration {
private static final String DEFAULT_GROUP = "_DEFAULT_";
private final MySQL mysql;
private final Map<String, Map<String, Object>> values = new HashMap<>();
@@ -23,19 +23,18 @@ public class MySQLGroupConfiguration implements PropertyConfiguration {
CachedRowSet crs = mysql.getCRS("select * from tab_groups");
while (crs.next()) {
String group = crs.getString("group");
if (!group.equals(DEFAULT_GROUP)) group = group.toLowerCase(Locale.US);
if (!group.equals(TabConstants.DEFAULT_GROUP)) group = group.toLowerCase(Locale.US);
String property = crs.getString("property");
String value = crs.getString("value");
String world = crs.getString("world");
String server = crs.getString("server");
TAB.getInstance().debug("Loaded group: " + String.format("%s, %s, %s, %s, %s", group, property, value, world, server));
setProperty0(group, property, server, world, value);
}
}
@Override
public void setProperty(String group, String property, String server, String world, String value) {
String lowercaseGroup = group.equals(DEFAULT_GROUP) ? group : group.toLowerCase(Locale.US);
String lowercaseGroup = group.equals(TabConstants.DEFAULT_GROUP) ? group : group.toLowerCase(Locale.US);
try {
if (getProperty(lowercaseGroup, property, server, world) != null) {
mysql.execute("delete from `tab_groups` where `group` = ? and `property` = ? and world " + querySymbol(world == null) + " ? and server " + querySymbol(server == null) + " ?", lowercaseGroup, property, world, server);
@@ -63,25 +62,25 @@ public class MySQLGroupConfiguration implements PropertyConfiguration {
@Override
public String[] getProperty(String group, String property, String server, String world) {
String lowercaseGroup = group.equals(DEFAULT_GROUP) ? group : group.toLowerCase(Locale.US);
String lowercaseGroup = group.equals(TabConstants.DEFAULT_GROUP) ? group : group.toLowerCase(Locale.US);
Object value;
if ((value = perWorld.getOrDefault(world, new HashMap<>()).getOrDefault(lowercaseGroup, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,world=%s", lowercaseGroup, world)};
}
if ((value = perWorld.getOrDefault(world, new HashMap<>()).getOrDefault(DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,world=%s", DEFAULT_GROUP, world)};
if ((value = perWorld.getOrDefault(world, new HashMap<>()).getOrDefault(TabConstants.DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,world=%s", TabConstants.DEFAULT_GROUP, world)};
}
if ((value = perServer.getOrDefault(server, new HashMap<>()).getOrDefault(lowercaseGroup, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,server=%s", lowercaseGroup, server)};
}
if ((value = perServer.getOrDefault(server, new HashMap<>()).getOrDefault(DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,server=%s", DEFAULT_GROUP, server)};
if ((value = perServer.getOrDefault(server, new HashMap<>()).getOrDefault(TabConstants.DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s,server=%s", TabConstants.DEFAULT_GROUP, server)};
}
if ((value = values.getOrDefault(lowercaseGroup, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s", lowercaseGroup)};
}
if ((value = values.getOrDefault(DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s", DEFAULT_GROUP)};
if ((value = values.getOrDefault(TabConstants.DEFAULT_GROUP, new HashMap<>()).get(property)) != null) {
return new String[] {toString(value), String.format("group=%s", TabConstants.DEFAULT_GROUP)};
}
return new String[0];
}
@@ -22,13 +22,12 @@ public class BelowName extends TabFeature {
public static final String OBJECTIVE_NAME = "TAB-BelowName";
public static final int DISPLAY_SLOT = 2;
private final String rawNumber = TAB.getInstance().getConfiguration().getConfig().getString("belowname-objective.number", "%health%");
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();
public BelowName() {
super("BelowName", "Updating BelowName number", TAB.getInstance().getConfiguration().getConfig().getStringList("belowname-objective.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("belowname-objective.disable-in-worlds"));
super("BelowName", "Updating BelowName number", "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)));
}
@@ -107,6 +106,7 @@ public class BelowName extends TabFeature {
onJoin(p);
return;
}
if (disabledNow) return;
int number = getValue(p);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
if (sameServerAndWorld(all, p)) {
@@ -21,8 +21,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager {
private final List<Object> serverGroups = new ArrayList<>(TAB.getInstance().getConfig().getConfigurationSection("header-footer.per-server").keySet());
public HeaderFooter() {
super("Header/Footer", "Updating header/footer", TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.disable-in-worlds"));
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)));
}
@@ -2,7 +2,6 @@ package me.neznamy.tab.shared.features;
import java.util.Collections;
import java.util.Objects;
import java.util.WeakHashMap;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
@@ -12,6 +11,7 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.nametags.NameTag;
@@ -20,8 +20,6 @@ import me.neznamy.tab.shared.features.redis.RedisSupport;
public class NickCompatibility extends TabFeature {
private final WeakHashMap<TabPlayer, String> nickedPlayers = new WeakHashMap<>();
private final WeakHashMap<RedisPlayer, String> nickedRedisPlayers = new WeakHashMap<>();
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);
@@ -43,38 +41,24 @@ public class NickCompatibility extends TabFeature {
if (packet.getAction() != EnumPlayerInfoAction.ADD_PLAYER) return;
for (PlayerInfoData data : packet.getEntries()) {
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(data.getUniqueId());
if (packetPlayer != null && packetPlayer != receiver) {
if (!packetPlayer.getName().equals(data.getName())) {
if (!nickedPlayers.containsKey(packetPlayer)) {
nickedPlayers.put(packetPlayer, data.getName());
TAB.getInstance().debug("Processing name change of player " + packetPlayer.getName() + " to " + data.getName());
processNameChange(packetPlayer, data.getName());
}
} else if (nickedPlayers.containsKey(packetPlayer)) {
nickedPlayers.remove(packetPlayer);
TAB.getInstance().debug("Processing name restore of player " + packetPlayer.getName());
processNameChange(packetPlayer, data.getName());
}
if (packetPlayer != null && packetPlayer != receiver && !packetPlayer.getNickname().equals(data.getName())) {
((ITabPlayer)packetPlayer).setNickname(data.getName());
TAB.getInstance().debug("Processing name change of player " + packetPlayer.getName() + " to " + data.getName());
processNameChange(packetPlayer);
}
if (redis != null) {
RedisPlayer redisPlayer = redis.getRedisPlayers().get(data.getUniqueId().toString());
if (redisPlayer == null) continue;
if (!redisPlayer.getName().equals(data.getName())) {
if (!nickedRedisPlayers.containsKey(redisPlayer)) {
nickedRedisPlayers.put(redisPlayer, data.getName());
TAB.getInstance().debug("Processing name change of redis player " + redisPlayer.getName() + " to " + data.getName());
processNameChange(redisPlayer, data.getName());
}
} else if (nickedRedisPlayers.containsKey(redisPlayer)) {
nickedRedisPlayers.remove(redisPlayer);
TAB.getInstance().debug("Processing name restore of redis player " + redisPlayer.getName());
processNameChange(redisPlayer, data.getName());
if (!redisPlayer.getNickName().equals(data.getName())) {
redisPlayer.setNickName(data.getName());
TAB.getInstance().debug("Processing name change of redis player " + redisPlayer.getName() + " to " + data.getName());
processNameChange(redisPlayer);
}
}
}
}
private void processNameChange(TabPlayer player, String name) {
private void processNameChange(TabPlayer player) {
TAB.getInstance().getCPUManager().runMeasuredTask(this, TabConstants.CpuUsageCategory.PACKET_PLAYER_INFO, () -> {
if (nameTags != null && !nameTags.hasTeamHandlingPaused(player)) {
@@ -83,27 +67,26 @@ public class NickCompatibility extends TabFeature {
String replacedPrefix = player.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String replacedSuffix = player.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(player.getTeamName(), replacedPrefix, replacedSuffix, nameTags.translate(nameTags.getTeamVisibility(player, viewer)),
nameTags.translate(nameTags.getCollisionManager().getCollision(player)), Collections.singletonList(name), 0), this);
nameTags.translate(nameTags.getCollisionManager().getCollision(player)), Collections.singletonList(player.getNickname()), 2), this);
}
}
if (belowname != null) {
int value = belowname.getValue(player);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getWorld().equals(player.getWorld()) && Objects.equals(all.getServer(), player.getServer()))
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, BelowName.OBJECTIVE_NAME, getNickname(player), value), this);
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, BelowName.OBJECTIVE_NAME, player.getNickname(), value), this);
}
}
if (yellownumber != null) {
int value = yellownumber.getValue(player);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, YellowNumber.OBJECTIVE_NAME, getNickname(player), value), this);
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, YellowNumber.OBJECTIVE_NAME, player.getNickname(), value), this);
}
}
});
}
private void processNameChange(RedisPlayer player, String name) {
player.setNickName(name);
private void processNameChange(RedisPlayer player) {
TAB.getInstance().getCPUManager().runMeasuredTask(this, TabConstants.CpuUsageCategory.PACKET_PLAYER_INFO, () -> {
if (nameTags != null) {
@@ -129,8 +112,4 @@ public class NickCompatibility extends TabFeature {
}
});
}
public String getNickname(TabPlayer player) {
return nickedPlayers.getOrDefault(player, player.getName());
}
}
@@ -21,7 +21,7 @@ import me.neznamy.tab.shared.TAB;
public abstract class PipelineInjector extends TabFeature {
//name of the pipeline decoder injected in netty
public static final String DECODER_NAME = "TAB";
public final String DECODER_NAME = "TAB";
//handler to inject before
private final String injectPosition;
@@ -62,7 +62,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
Map<TabPlayer, Set<TabFeature>> forceUpdate = new HashMap<>(size);
boolean somethingChanged = false;
for (Placeholder placeholder : usedPlaceholders) {
if (placeholder.getRefresh() == -1 || placeholder.isTriggerMode() || loopTime % placeholder.getRefresh() != 0) continue;
if (placeholder.getRefresh() == -1 || loopTime % placeholder.getRefresh() != 0) continue;
if (placeholder instanceof RelationalPlaceholderImpl && updateRelationalPlaceholder((RelationalPlaceholderImpl) placeholder, forceUpdate)) somethingChanged = true;
if (placeholder instanceof PlayerPlaceholderImpl && updatePlayerPlaceholder((PlayerPlaceholderImpl) placeholder, update)) somethingChanged = true;
if (placeholder instanceof ServerPlaceholderImpl && updateServerPlaceholder((ServerPlaceholderImpl) placeholder, update)) somethingChanged = true;
@@ -119,7 +119,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
long startTime = System.nanoTime();
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (placeholder.update(all)) {
if (placeholder.getIdentifier().equals("%vanished%")) TAB.getInstance().getFeatureManager().onVanishStatusChange(all);
if (placeholder.getIdentifier().equals(TabConstants.Placeholder.VANISHED)) TAB.getInstance().getFeatureManager().onVanishStatusChange(all);
update.computeIfAbsent(all, k -> new HashSet<>()).addAll(placeholderUsage.get(placeholder.getIdentifier()));
somethingChanged = true;
}
@@ -250,7 +250,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
tabExpansion.setPlaceholderValue(all, p.getIdentifier(), p.getLastValue(all));
}
}
if (p.getRefresh() % 50 == 0 && p.getRefresh() > 0 && !p.isTriggerMode()) {
if (p.getRefresh() % 50 == 0 && p.getRefresh() > 0) {
int refresh = gcd(p.getRefresh(), refreshTask.getInterval());
if (refreshTask.getInterval() != refresh) {
TAB.getInstance().debug("Decreasing refresh interval of placeholder refreshing task to " + refresh + "ms due to placeholder " + identifier);
@@ -294,4 +294,14 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
public void setTabExpansion(TabExpansion tabExpansion) {
this.tabExpansion = tabExpansion;
}
@Override
public void onJoin(TabPlayer connectedPlayer) {
if (tabExpansion == null) return;
for (Placeholder p : usedPlaceholders) {
if (p instanceof ServerPlaceholder) { // server placeholders don't update on join
tabExpansion.setPlaceholderValue(connectedPlayer, p.getIdentifier(), ((ServerPlaceholder) p).getLastValue());
}
}
}
}
@@ -13,6 +13,7 @@ import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.layout.Layout;
@@ -39,8 +40,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", TAB.getInstance().getConfiguration().getConfig().getStringList("tablist-name-formatting.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("tablist-name-formatting.disable-in-worlds"));
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));
}
@@ -233,67 +233,79 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
@Override
public void setPrefix(TabPlayer player, String prefix) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TABPREFIX).setTemporaryValue(prefix);
updatePlayer(player, true);
}
@Override
public void setName(TabPlayer player, String customName) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.CUSTOMTABNAME).setTemporaryValue(customName);
updatePlayer(player, true);
}
@Override
public void setSuffix(TabPlayer player, String suffix) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TABSUFFIX).setTemporaryValue(suffix);
updatePlayer(player, true);
}
@Override
public void resetPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TABPREFIX).setTemporaryValue(null);
updatePlayer(player, true);
}
@Override
public void resetName(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.CUSTOMTABNAME).setTemporaryValue(null);
updatePlayer(player, true);
}
@Override
public void resetSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TABSUFFIX).setTemporaryValue(null);
updatePlayer(player, true);
}
@Override
public String getCustomPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TABPREFIX).getTemporaryValue();
}
@Override
public String getCustomName(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.CUSTOMTABNAME).getTemporaryValue();
}
@Override
public String getCustomSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TABSUFFIX).getTemporaryValue();
}
@Override
public String getOriginalPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TABPREFIX).getOriginalRawValue();
}
@Override
public String getOriginalName(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.CUSTOMTABNAME).getOriginalRawValue();
}
@Override
public String getOriginalSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TABSUFFIX).getOriginalRawValue();
}
}
@@ -4,17 +4,33 @@ import me.neznamy.tab.api.TabPlayer;
public interface TabExpansion {
void setScoreboardVisible(TabPlayer player, boolean visible);
default void setScoreboardVisible(TabPlayer player, boolean visible) {
setValue(player, "scoreboard_visible", visible ? "Enabled" : "Disabled");
}
void setScoreboardName(TabPlayer player, String name);
default void setScoreboardName(TabPlayer player, String name) {
setValue(player, "scoreboard_name", name);
}
void setBossBarVisible(TabPlayer player, boolean visible);
default void setBossBarVisible(TabPlayer player, boolean visible) {
setValue(player, "bossbar_visible", visible ? "Enabled" : "Disabled");
}
void setNameTagPreview(TabPlayer player, boolean previewing);
default void setNameTagPreview(TabPlayer player, boolean previewing) {
setValue(player, "ntpreview", previewing ? "Enabled" : "Disabled");
}
void setPlaceholderValue(TabPlayer player, String placeholder, String value);
default void setPlaceholderValue(TabPlayer player, String placeholder, String value) {
setValue(player, "placeholder_" + placeholder.substring(1, placeholder.length()-1), value);
}
void setPropertyValue(TabPlayer player, String property, String value);
default void setPropertyValue(TabPlayer player, String property, String value) {
setValue(player, property, value);
}
void setRawPropertyValue(TabPlayer player, String property, String value);
default void setRawPropertyValue(TabPlayer player, String property, String value) {
setValue(player, property + "_raw", value);
}
void setValue(TabPlayer player, String key, String value);
}
@@ -29,18 +29,17 @@ public class YellowNumber extends TabFeature {
private static final String TITLE = "PlayerListObjectiveTitle";
/** Numeric value to display */
private final String rawValue = TAB.getInstance().getConfiguration().getConfig().getString("yellow-number-in-tablist.value", "%ping%");
private final String rawValue = TAB.getInstance().getConfiguration().getConfig().getString("yellow-number-in-tablist.value", TabConstants.Placeholder.PING);
/** Display type, either INTEGER or HEARTS */
private final EnumScoreboardHealthDisplay displayType = "%health%".equals(rawValue) || "%player_health%".equals(rawValue) ||
private final EnumScoreboardHealthDisplay displayType = TabConstants.Placeholder.HEALTH.equals(rawValue) || "%player_health%".equals(rawValue) ||
"%player_health_rounded%".equals(rawValue) ? EnumScoreboardHealthDisplay.HEARTS : EnumScoreboardHealthDisplay.INTEGER;
/**
* Constructs new instance and sends debug message that feature loaded.
*/
public YellowNumber() {
super("Yellow number", "Updating value", TAB.getInstance().getConfiguration().getConfig().getStringList("yellow-number-in-tablist.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("yellow-number-in-tablist.disable-in-worlds"));
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));
}
@@ -10,9 +10,9 @@ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
/**
@@ -20,22 +20,22 @@ import java.util.stream.Collectors;
*/
public class AlignedPlayerList extends PlayerList {
private final WeakHashMap<TabPlayer, PlayerView> playerViews = new WeakHashMap<>();
private final byte[] widths = new byte[65536];
private final Map<TabPlayer, PlayerView> playerViews = new HashMap<>();
private final byte[] widths = loadWidths();
public AlignedPlayerList() {
loadWidths();
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList("%vanished%"));
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
}
/**
* Loads widths from included widths.txt file as well as width overrides from config
*/
private void loadWidths() {
private byte[] loadWidths() {
byte[] widths = new byte[65536];
InputStream file = getClass().getClassLoader().getResourceAsStream("widths.txt");
if (file == null) {
TAB.getInstance().getErrorManager().criticalError("Failed to load widths.txt file. Is it inside the jar? Aligned suffix will not work.", null);
return;
return widths;
}
int characterId = 1;
for (String line : new BufferedReader(new InputStreamReader(file)).lines().collect(Collectors.toList())) {
@@ -43,8 +43,9 @@ public class AlignedPlayerList extends PlayerList {
}
Map<Integer, Integer> widthOverrides = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("tablist-name-formatting.character-width-overrides");
for (Entry<Integer, Integer> entry : widthOverrides.entrySet()) {
widths[entry.getKey()] = (byte)(int)entry.getValue();
widths[entry.getKey()] = entry.getValue().byteValue();
}
return widths;
}
@Override
@@ -84,6 +85,7 @@ public class AlignedPlayerList extends PlayerList {
public void onQuit(TabPlayer p) {
super.onQuit(p);
playerViews.values().forEach(v -> v.processPlayerQuit(p));
playerViews.remove(p);
}
@Override
@@ -180,7 +180,7 @@ public class PlayerView {
TabPlayer newMaxPlayer = null;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == ignoredPlayer) continue;
if (all.isVanished() && !canSeeVanished) continue;
if (all.isVanished() && !canSeeVanished && all != viewer) continue;
int localWidth = playerWidths.get(all);
if (localWidth > newMaxWidth) {
newMaxWidth = localWidth;
@@ -80,7 +80,7 @@ public class BossBarLine implements BossBar {
this.name = name;
this.displayCondition = Condition.getCondition(displayCondition);
if (this.displayCondition != null) {
manager.addUsedPlaceholders(Collections.singletonList("%condition:" + this.displayCondition.getName() + "%"));
manager.addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(this.displayCondition.getName())));
}
this.color = color;
this.style = style;
@@ -10,6 +10,7 @@ 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.shared.TabConstants;
import me.neznamy.tab.shared.features.TabExpansion;
/**
@@ -35,7 +36,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
private final List<BossBar> announcements = new ArrayList<>();
//players with toggled BossBar
private List<String> bossBarOffPlayers = new ArrayList<>();
private final List<String> bossBarOffPlayers;
//time when BossBar announce ends, used for placeholder
private long announceEndTime;
@@ -46,8 +47,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
* Constructs new instance and loads configuration
*/
public BossBarManagerImpl() {
super("BossBar", "Processing display conditions", TAB.getInstance().getConfiguration().getConfig().getStringList("bossbar.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("bossbar.disable-in-worlds"));
super("BossBar", "Processing display conditions", "bossbar");
for (Object bar : TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("bossbar.bars").keySet()){
BossBarLine line = loadFromConfig(bar.toString());
lines.put(bar.toString(), line);
@@ -56,8 +56,10 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
lineValues = lines.values().toArray(new BossBar[0]);
if (rememberToggleChoice) {
bossBarOffPlayers = TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("bossbar-off", new ArrayList<>());
} else {
bossBarOffPlayers = Collections.emptyList();
}
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder("%countdown%", 100, () -> (announceEndTime - System.currentTimeMillis()) / 1000);
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));
}
@@ -29,7 +29,7 @@ public class GlobalPlayerList extends TabFeature {
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("%vanished%"));
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
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));
}
@@ -12,7 +12,7 @@ public class LatencyRefresher extends TabFeature {
public LatencyRefresher() {
super("Global PlayerList", "Updating latency");
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%ping%", this);
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder(TabConstants.Placeholder.PING, this);
}
@Override
@@ -13,17 +13,18 @@ public class LayoutLatencyRefresher extends TabFeature {
public LayoutLatencyRefresher(LayoutManager manager) {
super(manager.getFeatureName(), "Updating latency");
this.manager = manager;
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%ping%", this);
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder(TabConstants.Placeholder.PING, this);
}
@Override
public void refresh(TabPlayer p, boolean force) {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
PlayerSlot slot = manager.getPlayerViews().get(all).getSlot(p);
if (slot != null) {
all.sendCustomPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY,
new PacketPlayOutPlayerInfo.PlayerInfoData(slot.getUUID(), p.getPing())), TabConstants.PacketCategory.LAYOUT_LATENCY);
}
Layout layout = manager.getPlayerViews().get(all);
if (layout == null) continue;
PlayerSlot slot = layout.getSlot(p);
if (slot == null) continue;
all.sendCustomPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY,
new PacketPlayOutPlayerInfo.PlayerInfoData(slot.getUUID(), p.getPing())), TabConstants.PacketCategory.LAYOUT_LATENCY);
}
}
}
@@ -24,18 +24,18 @@ public class LayoutManager extends TabFeature {
private final int emptySlotPing = TAB.getInstance().getConfiguration().getLayout().getInt("empty-slot-ping-value", 1000);
private final SkinManager skinManager = new SkinManager(defaultSkin);
private final Map<String, Layout> layouts = new LinkedHashMap<>();
private final Map<Integer, UUID> uuids = new HashMap<Integer, UUID>(){{
for (int slot=1; slot<=80; slot++) {
put(slot, new UUID(0, translateSlot(slot)));
}
}};
private final Map<String, Layout> layouts = loadLayouts();
private final WeakHashMap<TabPlayer, Layout> playerViews = new WeakHashMap<>();
private final Map<Integer, UUID> uuids = new HashMap<>();
private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(TabPlayer::getTeamName)));
public LayoutManager() {
super("Layout", "Switching layouts");
for (int slot=1; slot<=80; slot++) {
uuids.put(slot, new UUID(0, translateSlot(slot)));
}
loadLayouts();
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList("%vanished%"));
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");
}
@@ -50,11 +50,12 @@ public class LayoutManager extends TabFeature {
}
@SuppressWarnings("unchecked")
private void loadLayouts() {
private Map<String, Layout> loadLayouts() {
Map<String, Layout> layoutMap = new LinkedHashMap<>();
for (Entry<Object, Object> layout : TAB.getInstance().getConfiguration().getLayout().getConfigurationSection("layouts").entrySet()) {
Map<String, Object> map = (Map<String, Object>) layout.getValue();
Condition displayCondition = Condition.getCondition((String) map.get("condition"));
if (displayCondition != null) addUsedPlaceholders(Collections.singletonList("%condition:" + displayCondition.getName() + "%"));
if (displayCondition != null) addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(displayCondition.getName())));
Map<Integer, FixedSlot> fixedSlots = new HashMap<>();
List<Integer> emptySlots = new ArrayList<>();
List<ParentGroup> parentGroups = new ArrayList<>();
@@ -87,9 +88,10 @@ public class LayoutManager extends TabFeature {
parentGroups.add(new ParentGroup(l, condition, positions.stream().mapToInt(i->i).toArray()));
emptySlots.removeAll(positions);
}
layouts.put(layout.getKey().toString(), l);
layoutMap.put(layout.getKey().toString(), l);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layout(layout.getKey().toString()), l);
}
return layoutMap;
}
@Override
@@ -4,6 +4,7 @@ import java.util.*;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.placeholders.conditions.Condition;
public class ParentGroup {
@@ -18,7 +19,7 @@ public class ParentGroup {
this.layout = layout;
this.condition = condition;
if (condition != null) {
layout.addUsedPlaceholders(Collections.singletonList("%condition:" + condition.getName() + "%"));
layout.addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(condition.getName())));
}
this.slots = slots;
for (int slot : slots) {
@@ -3,9 +3,11 @@ package me.neznamy.tab.shared.features.nametags;
import java.util.Collections;
import java.util.WeakHashMap;
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.shared.TabConstants;
public class CollisionManager extends TabFeature {
@@ -20,15 +22,15 @@ public class CollisionManager extends TabFeature {
this.collisionRule = collisionRule;
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("LibsDisguises") == null && !TAB.getInstance().getPlatform().isProxy()) return; //no disguise plugin available
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%collision%", 500, p -> {
if (TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.LIBS_DISGUISES) == null && TAB.getInstance().getServerVersion() != ProtocolVersion.PROXY) return; //no disguise plugin available
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.COLLISION, 500, p -> {
if (forcedCollision.containsKey(p)) return forcedCollision.get(p);
boolean newCollision = !p.isDisguised();
collision.put(p, newCollision);
return newCollision;
});
addUsedPlaceholders(Collections.singletonList("%collision%"));
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.COLLISION));
}
public boolean getCollision(TabPlayer p) {
@@ -3,10 +3,12 @@ package me.neznamy.tab.shared.features.nametags;
import java.util.*;
import me.neznamy.tab.api.Property;
import me.neznamy.tab.api.ProtocolVersion;
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.shared.TabConstants;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -27,14 +29,13 @@ 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 final boolean accepting18x = TAB.getInstance().getPlatform().isProxy() ||
private final boolean accepting18x = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ||
TAB.getInstance().getPlatform().getPluginVersion("ViaRewind") != null ||
TAB.getInstance().getPlatform().getPluginVersion("ProtocolSupport") != null ||
TAB.getInstance().getServerVersion().getMinorVersion() == 8;
public NameTag() {
super("NameTags", "Updating prefix/suffix", TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard-teams.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard-teams.disable-in-worlds"));
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);
@@ -237,7 +238,7 @@ public class NameTag extends TabFeature implements TeamManager {
String currentPrefix = tagPrefix.getFormat(viewer);
String currentSuffix = tagSuffix.getFormat(viewer);
boolean visible = getTeamVisibility(p, viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), 0), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), 2), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
}
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());
@@ -247,7 +248,7 @@ public class NameTag extends TabFeature implements TeamManager {
boolean visible = getTeamVisibility(p, viewer);
String currentPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String currentSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), 0), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), 2), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
}
public void unregisterTeam(TabPlayer p) {
@@ -267,11 +268,8 @@ public class NameTag extends TabFeature implements TeamManager {
if (hasTeamHandlingPaused(p)) return;
String replacedPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String replacedSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
if (viewer.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().isUnregisterBeforeRegister()) {
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()), TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
}
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), replacedPrefix, replacedSuffix, translate(getTeamVisibility(p, viewer)),
translate(collisionManager.getCollision(p)), Collections.singletonList(p.getNickname()), 0), TabConstants.PacketCategory.NAMETAGS_TEAM_REGISTER);
translate(collisionManager.getCollision(p)), Collections.singletonList(p.getNickname()), 2), TabConstants.PacketCategory.NAMETAGS_TEAM_REGISTER);
}
private void updateTeam(TabPlayer p) {
@@ -315,45 +313,53 @@ public class NameTag extends TabFeature implements TeamManager {
@Override
public void setPrefix(TabPlayer player, String prefix) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(prefix);
updateTeamData(player);
}
@Override
public void setSuffix(TabPlayer player, String suffix) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(suffix);
updateTeamData(player);
}
@Override
public void resetPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(null);
updateTeamData(player);
}
@Override
public void resetSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(null);
updateTeamData(player);
}
@Override
public String getCustomPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TAGPREFIX).getTemporaryValue();
}
@Override
public String getCustomSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TAGSUFFIX).getTemporaryValue();
}
@Override
public String getOriginalPrefix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TAGPREFIX).getOriginalRawValue();
}
@Override
public String getOriginalSuffix(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.TAGSUFFIX).getOriginalRawValue();
}
@@ -5,6 +5,7 @@ import java.util.Collections;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
public class VisibilityRefresher extends TabFeature {
@@ -13,8 +14,8 @@ public class VisibilityRefresher extends TabFeature {
public VisibilityRefresher(NameTag nameTags) {
super(nameTags.getFeatureName(), "Updating NameTag visibility");
this.nameTags = nameTags;
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%invisible%", 500, TabPlayer::hasInvisibilityPotion);
addUsedPlaceholders(Collections.singletonList("%invisible%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.INVISIBLE, 500, TabPlayer::hasInvisibilityPotion);
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.INVISIBLE));
}
@Override
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.features.nametags.unlimited;
import me.neznamy.tab.api.ArmorStandManager;
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.shared.TabConstants;
import me.neznamy.tab.shared.features.TabExpansion;
@@ -227,6 +228,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void disableArmorStands(TabPlayer player) {
Preconditions.checkLoaded(player);
if (playersDisabledWithAPI.contains(player)) return;
playersDisabledWithAPI.add(player);
pauseArmorStands(player);
@@ -235,6 +237,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void enableArmorStands(TabPlayer player) {
Preconditions.checkLoaded(player);
if (!playersDisabledWithAPI.contains(player)) return;
playersDisabledWithAPI.remove(player);
resumeArmorStands(player);
@@ -248,6 +251,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void setName(TabPlayer player, String customName) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(customName);
rebuildNameTagLine(player);
getArmorStandManager(player).refresh(true);
@@ -255,6 +259,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void setLine(TabPlayer player, String line, String value) {
Preconditions.checkLoaded(player);
if (!getDefinedLines().contains(line)) throw new IllegalArgumentException("\"" + line + "\" is not a defined line. Defined lines: " + getDefinedLines());
player.getProperty(line).setTemporaryValue(value);
getArmorStandManager(player).refresh(true);
@@ -262,6 +267,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void resetName(TabPlayer player) {
Preconditions.checkLoaded(player);
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(null);
rebuildNameTagLine(player);
getArmorStandManager(player).refresh(true);
@@ -269,27 +275,32 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void resetLine(TabPlayer player, String line) {
Preconditions.checkLoaded(player);
player.getProperty(line).setTemporaryValue(null);
getArmorStandManager(player).refresh(true);
}
@Override
public String getCustomName(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getTemporaryValue();
}
@Override
public String getCustomLineValue(TabPlayer player, String line) {
Preconditions.checkLoaded(player);
return player.getProperty(line).getTemporaryValue();
}
@Override
public String getOriginalName(TabPlayer player) {
Preconditions.checkLoaded(player);
return player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getOriginalRawValue();
}
@Override
public String getOriginalLineValue(TabPlayer player, String line) {
Preconditions.checkLoaded(player);
return player.getProperty(line).getOriginalRawValue();
}
@@ -309,6 +320,7 @@ 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);
@@ -316,6 +328,7 @@ 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);
@@ -323,6 +336,7 @@ 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);
@@ -330,6 +344,7 @@ 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);
@@ -337,6 +352,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void pauseTeamHandling(TabPlayer player) {
Preconditions.checkLoaded(player);
if (teamHandlingPaused.contains(player)) return;
if (!isDisabledPlayer(player)) unregisterTeam(player);
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
@@ -345,6 +361,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
@Override
public void resumeTeamHandling(TabPlayer player) {
Preconditions.checkLoaded(player);
if (!teamHandlingPaused.contains(player)) return;
teamHandlingPaused.remove(player); //removing before, so registerTeam method runs
if (!isDisabledPlayer(player)) registerTeam(player);
@@ -2,16 +2,17 @@ package me.neznamy.tab.shared.features.nametags.unlimited;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.proxy.PluginMessageHandler;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
public class ProxyNameTagX extends NameTagX {
private final PluginMessageHandler plm;
private final PluginMessageHandler plm = ((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler();
public ProxyNameTagX(PluginMessageHandler plm) {
public ProxyNameTagX() {
super(ProxyArmorStandManager::new);
this.plm = plm;
}
@Override
@@ -137,12 +137,12 @@ public class RedisPlayer {
public PacketPlayOutScoreboardTeam getRegisterTeamPacket() {
if (disabledNameTags) return null;
return new PacketPlayOutScoreboardTeam(teamName, tagPrefix, tagSuffix, nameVisibility ? "always" : "never", "always", Collections.singletonList(nickname), 0);
return new PacketPlayOutScoreboardTeam(teamName, tagPrefix, tagSuffix, nameVisibility ? "always" : "never", "always", Collections.singletonList(nickname), 2);
}
public PacketPlayOutScoreboardTeam getUpdateTeamPacket() {
if (disabledNameTags) return null;
return new PacketPlayOutScoreboardTeam(teamName, tagPrefix, tagSuffix, nameVisibility ? "always" : "never", "always", 0);
return new PacketPlayOutScoreboardTeam(teamName, tagPrefix, tagSuffix, nameVisibility ? "always" : "never", "always", 2);
}
public PacketPlayOutScoreboardTeam getUnregisterTeamPacket() {
@@ -44,10 +44,10 @@ public abstract class RedisSupport extends TabFeature {
*/
protected RedisSupport() {
super("RedisBungee", null);
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder("%online%", 1000, () ->
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("%staffonline%", 1000, () ->
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());
}
@@ -59,7 +59,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
this(manager, name, title, lines, false);
this.displayCondition = Condition.getCondition(displayCondition);
if (this.displayCondition != null) {
manager.addUsedPlaceholders(Collections.singletonList("%condition:" + this.displayCondition.getName() + "%"));
manager.addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.condition(this.displayCondition.getName())));
}
}
@@ -102,6 +102,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
* @return most optimal line from provided text
*/
private ScoreboardLine registerLine(int lineNumber, String text) {
if (text == null) return new StaticLine(this, lineNumber, "");
if (text.startsWith("Custom|")) {
String[] elements = text.split("\\|");
return new CustomLine(this, lineNumber, elements[1], elements[2], elements[3], Integer.parseInt(elements[4]));
@@ -109,7 +110,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
if (text.startsWith("Long|")) {
return new LongLine(this, lineNumber, text.substring(5));
}
if (text.contains("%") || (manager.isUsingNumbers() && text.length() <= 26)) {
if (text.contains("%")) {
return new StableDynamicLine(this, lineNumber, text);
}
return new StaticLine(this, lineNumber, text);
@@ -56,8 +56,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
*/
@SuppressWarnings("unchecked")
public ScoreboardManagerImpl() {
super("Scoreboard", "Switching displayed scoreboard", TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard.disable-in-servers"),
TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard.disable-in-worlds"));
super("Scoreboard", "Switching displayed scoreboard", "scoreboard");
if (rememberToggleChoice) {
sbOffPlayers = Collections.synchronizedList(new ArrayList<>(TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("scoreboard-off", new ArrayList<>())));
} else {
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.features.scoreboard.lines;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardImpl;
@@ -39,7 +40,7 @@ public class LongLine extends ScoreboardLine {
"", "always", "always", 0), TabConstants.PacketCategory.SCOREBOARD_LINES);
} else {
removeLine(refreshed, refreshed.getProperty(nameProperty).get());
String[] values = splitText(getPlayerName(lineNumber), refreshed.getProperty(textProperty).get(), refreshed.getVersion().getMinorVersion() >= 8 ? 40 : 16);
String[] values = splitText(getPlayerName(lineNumber), RGBUtils.getInstance().convertRGBtoLegacy(refreshed.getProperty(textProperty).get()), refreshed.getVersion().getMinorVersion() >= 8 ? 40 : 16);
addLine(refreshed, values[1], values[0], values[2]);
refreshed.setProperty(this, nameProperty, values[1]);
}
@@ -54,7 +55,7 @@ public class LongLine extends ScoreboardLine {
addLine(p, playerName, value, "");
p.setProperty(this, nameProperty, playerName);
} else {
String[] values = splitText(playerName, value, p.getVersion().getMinorVersion() >= 8 ? 40 : 16);
String[] values = splitText(playerName, RGBUtils.getInstance().convertRGBtoLegacy(value), p.getVersion().getMinorVersion() >= 8 ? 40 : 16);
addLine(p, values[1], values[0], values[2]);
p.setProperty(this, nameProperty, values[1]);
}
@@ -10,7 +10,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.api.scoreboard.Line;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore.Action;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardImpl;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
@@ -66,10 +65,24 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
*/
public abstract void unregister(TabPlayer p);
/**
* Returns forced name start of this line to specified viewer
*
* @return forced name start of this line to specified viewer
*/
public String getPlayerName(TabPlayer viewer) {
return playerName;
}
/**
* Returns forced name start of this line
*
* @return forced name start of this line
*/
public String getPlayerName() {
return playerName;
}
/**
* Splits the text into 2 with given max length of first string
*
@@ -85,15 +98,6 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
if (string.charAt(splitIndex-1) == EnumChatFormat.COLOR_CHAR) splitIndex--;
return new String[] {string.substring(0, splitIndex), string.substring(splitIndex)};
}
/**
* Returns forced name start of this player
*
* @return forced name start of this player
*/
public String getPlayerName() {
return playerName;
}
/**
* Builds forced name start based on line number
@@ -122,9 +126,6 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
*/
protected void addLine(TabPlayer p, String fakePlayer, String prefix, String suffix) {
p.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, ScoreboardManagerImpl.OBJECTIVE_NAME, fakePlayer, getNumber(p)), TabConstants.PacketCategory.SCOREBOARD_LINES);
if (p.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().isUnregisterBeforeRegister()) {
p.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName), TabConstants.PacketCategory.SCOREBOARD_LINES);
}
p.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName, prefix, suffix, "never", "never", Collections.singletonList(fakePlayer), 0), TabConstants.PacketCategory.SCOREBOARD_LINES);
}
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.features.scoreboard.lines;
import me.neznamy.tab.api.Property;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
@@ -17,7 +18,7 @@ import me.neznamy.tab.shared.features.scoreboard.ScoreboardImpl;
*/
public class StableDynamicLine extends ScoreboardLine {
private static final String[] EMPTY_ARRAY = new String[0];
private final String[] EMPTY_ARRAY = new String[0];
/**
* Constructs new instance with given parameters
@@ -109,7 +110,7 @@ public class StableDynamicLine extends ScoreboardLine {
*/
private String[] split(TabPlayer p, String text) {
int charLimit = 16;
if (!TAB.getInstance().getPlatform().isProxy() &&
if (TAB.getInstance().getServerVersion() != ProtocolVersion.PROXY &&
TAB.getInstance().getServerVersion().getMinorVersion() >= 13 &&
p.getVersion().getMinorVersion() < 13) {
//ProtocolSupport bug
@@ -137,7 +138,7 @@ public class StableDynamicLine extends ScoreboardLine {
public void setText(String text) {
this.text = text;
for (TabPlayer p : parent.getPlayers()) {
p.setProperty(this, teamName, text);
p.setProperty(this, parent.getName() + "-" + teamName, text);
refresh(p, true);
}
}
@@ -77,9 +77,11 @@ public class StaticLine extends ScoreboardLine {
@Override
public void setText(String text) {
setValues(text);
for (TabPlayer p : parent.getPlayers()) {
unregister(p);
}
setValues(text);
for (TabPlayer p : parent.getPlayers()) {
register(p);
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared.features.sorting.types;
import java.util.LinkedHashMap;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.sorting.Sorting;
/**
@@ -17,7 +18,7 @@ public class Groups extends SortingType {
* Constructs new instance
*/
public Groups(Sorting sorting, String options) {
super(sorting, "%group%");
super(sorting, TabConstants.Placeholder.GROUP);
sortedGroups = convertSortingElements(options.split(","));
}
@@ -20,12 +20,26 @@ public class Placeholder extends SortingType {
*
* @param sorting
* sorting feature
* @param options
* @param options
* options used by this sorting type
*/
public Placeholder(Sorting sorting, String options) {
super(sorting, options.split(":")[0]);
sortingMap = convertSortingElements(options.substring(options.indexOf(":")+1).split(","));
super(sorting, getPlaceholder(options));
String[] args = options.split(":");
sortingMap = convertSortingElements(args[args.length-1].split(","));
}
/**
* Returns placeholder identifier used in provided options. This allows
* support for placeholders that contain ":", such as conditions or animations.
*
* @param options
* Configured sorting options in "%placeholder%:values" format
* @return Placeholder configured in options
*/
private static String getPlaceholder(String options) {
String[] args = options.split(":");
return options.substring(0, options.length()-args[args.length-1].length()-1);
}
@Override
@@ -12,7 +12,7 @@ import me.neznamy.tab.shared.features.sorting.Sorting;
public abstract class SortingType {
//number to add to / subtract from to prevent incorrect sorting with negative values
protected static final int DEFAULT_NUMBER = 5000000;
protected final int DEFAULT_NUMBER = 5000000;
//placeholder to sort by, if sorting type uses it
protected String sortingPlaceholder;
@@ -14,7 +14,7 @@ import net.luckperms.api.query.QueryOptions;
*/
public class LuckPerms extends PermissionPlugin {
private static final String UPDATE_MESSAGE = "Upgrade to LuckPerms 5";
private final String UPDATE_MESSAGE = "Upgrade to LuckPerms 5";
/**
* Constructs new instance with given parameter
@@ -32,10 +32,10 @@ public class LuckPerms extends PermissionPlugin {
if (getVersion().startsWith("4")) return UPDATE_MESSAGE;
net.luckperms.api.LuckPerms api = LuckPermsProvider.get();
User user = api.getUserManager().getUser(p.getUniqueId());
if (user == null) return TabConstants.DEFAULT_GROUP; //pretend like nothing is wrong
if (user == null) return TabConstants.NO_GROUP; //pretend like nothing is wrong
return user.getPrimaryGroup();
} catch (Exception e) {
return TabConstants.DEFAULT_GROUP;
return TabConstants.NO_GROUP;
}
}
Loaded 100 of 116 files, more files were not shown because too many files have changed in this diff. Show more