Compare commits

..
28 Commits
Author SHA1 Message Date
NEZNAMY 8e5ea29c40 Define decimal separator in one more way to make sure it works properly (#967) 2023-07-07 15:25:11 +02:00
NEZNAMY 287b51a0bf Send warn if using debug command on player who is not loaded (#959) 2023-07-06 20:55:16 +02:00
NEZNAMY 28cbf952fa Send warn if both per world playerlist and layout are enabled 2023-07-06 15:12:28 +02:00
NEZNAMY 598c35a3aa Simplify 2023-07-06 12:37:24 +02:00
NEZNAMY b8b67a64ab Simplify nms loading 2023-07-06 12:33:47 +02:00
NEZNAMY 7f6020ef34 Fix loading on catserver 2023-07-06 12:13:12 +02:00
NEZNAMY aeed453e15 Move named field loading to respective classes 2023-07-05 23:41:54 +02:00
NEZNAMY 71dddcca4c Drop support for mojang mapped server jar 2023-07-05 21:49:54 +02:00
NEZNAMY 46cdf79e20 Use all arg constructor for spawn entity packet on 1.17+ (using the wrong packet still spawns the entity) 2023-07-05 21:12:47 +02:00
NEZNAMY d7af96823b Merge pull request #962 from MachineBreaker/patch-1
Fix ViaVersion bug returning -1 version
2023-07-04 23:11:08 +02:00
MachineBreaker 5a228bd6b3 Fix ViaVersion bug returning -1 version
Explanation:

Both TAB and ViaVersion PlayerJoinEvent priority are LOWEST and seems that TAB registers earlier than ViaVersion the listeners, this causes TAB not being able to get the client protocol version due to ViaVersion not being able to register it firstly.

There might be another way to workaround this since i don't know changing the priority would cause any issue with TAB itself.
2023-07-04 16:19:07 -04:00
NEZNAMY fcd95655ce Send warn when trying to use parse command without any placeholders 2023-07-04 11:09:14 +02:00
NEZNAMY 7f5b57f8d2 Merge pull request #956 from Tanguygab/master
Update skins in Layout's FixedSlots
2023-07-03 17:07:01 +02:00
NEZNAMY f596877716 Fix forwarding unlimited nametag disable status to backend 2023-07-02 17:30:58 +02:00
NEZNAMY 1802c8fd90 Fix debug command not showing properties from unlimited nametag as disabled when they are 2023-07-02 17:30:35 +02:00
Tanguygab e05856facc only update name if skin didn't update 2023-07-02 13:51:37 +02:00
Tanguygab 45cfc3abb0 simplified FixedSlot skin updating 2023-07-02 13:40:08 +02:00
Tanguygab 2d39579861 added support for placeholders in layout's fixed-slots' skins 2023-07-02 13:27:37 +02:00
NEZNAMY 431f4bb4d5 Change Velocity policy 2023-07-02 00:39:10 +02:00
NEZNAMY 7bc9d38176 Slightly improve debug messages 2023-07-01 20:15:50 +02:00
NEZNAMY 7536447a8f Update README.md 2023-06-29 20:47:33 +02:00
NEZNAMY 059b796061 Update CONTRIBUTING.md 2023-06-29 20:35:44 +02:00
NEZNAMY a89942484f Layout - Fix duplicate internal names causing issues 2023-06-28 21:03:13 +02:00
NEZNAMY c28be3fd65 Add (another) warn if plugin's CPU usage is high 2023-06-26 21:04:26 +02:00
NEZNAMY 5f1116795c Add Folia support (#919) 2023-06-25 12:11:41 +02:00
NEZNAMY 4f54d2f6b2 4.0.3-SNAPSHOT 2023-06-25 10:59:05 +02:00
NEZNAMY 6b7bedbe2d Fix NPE when placeholder returns null 2023-06-24 14:30:44 +02:00
NEZNAMY 20ff225c31 Add missing set team color 2023-06-23 22:03:32 +02:00
35 changed files with 354 additions and 397 deletions

No files matched your search

+1 -2
View File
@@ -6,5 +6,4 @@ is in quality of the report (typically providing correct steps to reproduce that
* Test **experimental builds**, which can be found in [GitHub Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
This way you will help make the actual releases more stable / help get stable releases sooner.
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples.
+8 -55
View File
@@ -1,70 +1,23 @@
# About
TAB aims to be a superior all-in-one minecraft plugin for displaying information that outperforms all
similar plugins in terms of features, performance, compatibility and support.
similar plugins in terms of features, performance and compatibility.
More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/Why-TAB%3F) wiki page.
# Distribution
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
version anymore. Only one version of the plugin exists now, which contains all implemented features
and is available to everyone for free. However, customer support is only provided to
customers who have purchased the plugin (see [Customer support](#customer-support)).
# Compiling
Compilation requires JDK 17 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
The universal jar contains all modules for all supported platforms.
# Customer support
Unlike with other projects where requirements and level of support provided are unclear,
we provide a clear answer to what kind of service you will or will not receive. Some projects don't
even clearly state if they are free or paid. While they are listed as paid on spigot, they have
a free download link in description, which can be confusing.
Support is also quite often "we have a discord server, but don't ping devs for support", suggesting
there is no official support, just the community.
In TAB, everything is clearly defined. **Downloads are free, but services are paid.**
The plugin is free to download for everyone. The only exception might be occasional update delays
for free users once a year.
Customer support, however, is only provided to customers who have formally purchased a copy
of the plugin on any of the websites the plugin is being sold on:
* [BuiltByBit](https://builtbybit.com/resources/14009/)
* [Polymart](https://polymart.org/resource/484)
After purchasing, you can request assistance via either private messages on the website where you
have purchased it, or using [TAB's Discord server](https://discord.gg/EaSvdk6) (which is highly
preferred, since the communication will be much faster thanks to notifications and more).
* We use a ticket system, which allows us to effectively track any amount of unresolved problems.
No more questions getting lost in the sea of messages from other people asking. Tickets are only closed
after user confirmed all of their questions were answered and then receive a transcript of the ticket
from the bot after ticket is closed into private messages (unless user is blocking them).
* We do not rely on the community helping, which often makes things even worse. We know people have
better things to do than helping random people with a random plugin, especially when most people only
have a very basic knowledge of the plugin, not being able to answer more complicated problems.
* We have notifications enabled and reply as soon as possible. We do not have the server muted unlike
most mods on most servers who only check it when they are really bored, give a wiki link and
disappear again.
* As the developer, I do not rely on helpers providing support instead of me. They are handy as
I am obviously not available 24/7, but I do not intentionally leave anything to them. I reply
whenever I am available without any intentional delays.
# Other links
Download the plugin for free:
* [GitHub releases](https://github.com/NEZNAMY/TAB/releases)
* [BuiltByBit](https://builtbybit.com/resources/20631/)
* [Modrinth](https://modrinth.com/plugin/tab-was-taken)
[Wiki](https://github.com/NEZNAMY/TAB/wiki)
# Documentation
You can find everything about the plugin on the [Wiki](https://github.com/NEZNAMY/TAB/wiki). This includes a detailed description
of all features, as well as information regarding compatibility or limitations of each feature.
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
In other words, they **are not available for**:
* **Support** - support is provided on discord for buyers, where roles are given to verified users
allowing us to know who bought it and who didn't. It also serves as a much faster way of communication.
* **Feature requests** - closed indefinitely.
**If you're a customer, please use discord for reporting bugs, especially if you aren't good at making bug reports.
You will be provided with a much faster and higher quality service.**
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is an issue tracker for all the kind of problems covered in issue templates.
Please refrain from attempting to use it to receive help with your server, such issues will be closed immediately.
When opening an issue, provide as much information as possible. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report. Bug reports where steps to reproduce are "idk" have no value and will not help to get the bug fixed at all.
# Contributing
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "4.0.2"
version = "4.0.3-SNAPSHOT"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -21,7 +21,7 @@ public class BukkitEventListener extends EventListener<Player> implements Listen
quit(e.getPlayer().getUniqueId());
}
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent e) {
join(e.getPlayer());
}
@@ -40,4 +40,4 @@ public class BukkitEventListener extends EventListener<Player> implements Listen
public TabPlayer createPlayer(Player player) {
return new BukkitTabPlayer(player);
}
}
}
@@ -1,20 +1,18 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.platform.FoliaPlatform;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.nms.storage.nms.*;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
/**
* Main class for Bukkit platform
@@ -30,7 +28,8 @@ public class BukkitTAB extends JavaPlugin {
Bukkit.getPluginManager().disablePlugin(this);
return;
}
BukkitPlatform platform = new BukkitPlatform(this);
BukkitPlatform platform = ReflectionUtils.classExists("io.papermc.paper.threadedregions.RegionizedServer") ?
new FoliaPlatform(this) : new BukkitPlatform(this);
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(version), getDataFolder()));
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
@@ -65,10 +64,12 @@ public class BukkitTAB extends JavaPlugin {
private boolean isVersionSupported() {
try {
long time = System.currentTimeMillis();
NMSStorage.setInstance(getNMSLoader());
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
NMSStorage.setInstance(minorVersion >= 17 ? new BukkitModernNMSStorage() : new BukkitLegacyNMSStorage());
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} catch (IllegalStateException ex) {
} catch (Exception ex) {
if (ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]) == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is not compatible. " +
"This plugin version was made for " + ProtocolVersion.values()[ProtocolVersion.values().length-1].getFriendlyName() +
@@ -80,19 +81,4 @@ public class BukkitTAB extends JavaPlugin {
return false;
}
}
private @NotNull NMSStorage getNMSLoader() {
List<Callable<NMSStorage>> loaders = Arrays.asList(
BukkitLegacyNMSStorage::new,
BukkitModernNMSStorage::new,
MojangModernNMSStorage::new,
ThermosNMSStorage::new
);
for (Callable<NMSStorage> loader : loaders) {
try {
return loader.call();
} catch (Exception ignored) {}
}
throw new IllegalStateException("Unsupported server version");
}
}
@@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.Getter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.chat.rgb.RGBUtils;
import me.neznamy.tab.shared.hook.ViaVersionHook;
@@ -1,6 +1,7 @@
package me.neznamy.tab.platforms.bukkit.features;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.platform.TabPlayer;
@@ -190,4 +191,9 @@ public class BukkitNameTagX extends BackendNameTagX implements Listener, PacketS
datawatcher.getHelper().setArmorStandFlags((byte)16);
return datawatcher;
}
@Override
public void runInEntityScheduler(Object entity, Runnable task) {
((BukkitPlatform)TAB.getInstance().getPlatform()).runEntityTask((Entity) entity, task);
}
}
@@ -21,8 +21,8 @@ public class DataWatcher implements EntityData {
/** NMS Fields */
public static Class<?> CLASS;
public static Constructor<?> CONSTRUCTOR;
public static Method REGISTER;
private static Constructor<?> CONSTRUCTOR;
private static Method REGISTER;
public static Method markDirty;
public static Method packDirty;
@@ -38,8 +38,16 @@ public class DataWatcher implements EntityData {
* @param nms
* NMS storage reference
*/
public static void load(NMSStorage nms) {
public static void load(NMSStorage nms) throws NoSuchMethodException {
CONSTRUCTOR = ReflectionUtils.getOnlyConstructor(CLASS);
if (nms.getMinorVersion() >= 9) {
REGISTER = ReflectionUtils.getMethod(CLASS, new String[]{"register", "a"}, DataWatcherObject.CLASS, Object.class); // {Bukkit, Bukkit 1.18+}
} else {
REGISTER = ReflectionUtils.getMethod(CLASS, new String[]{"func_75682_a", "a"}, int.class, Object.class); // {Thermos 1.7.10, Bukkit}
}
if (nms.getMinorVersion() >= 19) {
packDirty = CLASS.getMethod("b");
}
if (nms.is1_19_3Plus()) {
markDirty = ReflectionUtils.getOnlyMethod(CLASS, void.class, DataWatcherObject.CLASS);
}
@@ -52,6 +52,28 @@ public class DataWatcherHelper {
}
}
public static void load(NMSStorage nms) throws NoSuchFieldException, IllegalAccessException {
if (nms.getMinorVersion() < 9) return;
DataWatcherSerializer_BYTE = DataWatcherRegistry.getDeclaredField("a").get(null);
DataWatcherSerializer_FLOAT = DataWatcherRegistry.getDeclaredField("c").get(null);
DataWatcherSerializer_STRING = DataWatcherRegistry.getDeclaredField("d").get(null);
if (nms.is1_19_3Plus()) {
DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherRegistry.getDeclaredField("g").get(null);
if (nms.is1_19_4Plus()) {
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("k").get(null);
} else {
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("j").get(null);
}
} else {
if (nms.getMinorVersion() >= 13) {
DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherRegistry.getDeclaredField("f").get(null);
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("i").get(null);
} else {
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("h").get(null);
}
}
}
/**
* Writes entity byte flags
*
@@ -7,7 +7,6 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.*;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
@@ -39,7 +38,7 @@ public class BukkitLegacyNMSStorage extends NMSStorage {
}
/**
* Returns class from given name
* Returns class from given name. Supports modded servers, such as Thermos.
*
* @param name
* class name
@@ -48,40 +47,11 @@ public class BukkitLegacyNMSStorage extends NMSStorage {
* if class was not found
*/
public Class<?> getLegacyClass(@NotNull String name) throws ClassNotFoundException {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PacketScoreboard.ScoreboardScore_setScore = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardScoreClass, new String[] {"setScore", "c"}, int.class); // 1.5.1+, 1.5
PacketScoreboard.ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[] {"setAllowFriendlyFire", "a"}, boolean.class); // 1.5.1+, 1.5
PacketScoreboard.ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[] {"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // 1.5.1+, 1.5
if (minorVersion >= 7) {
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
}
if (minorVersion >= 8) {
PacketScoreboard.ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[] {"setNameTagVisibility", "a"}, PacketScoreboard.EnumNameTagVisibility); // {1.8.1+, 1.8}
}
if (minorVersion >= 9) {
DataWatcher.REGISTER = DataWatcher.CLASS.getMethod("register", DataWatcherObject.CLASS, Object.class);
DataWatcherHelper.DataWatcherSerializer_BYTE = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("a").get(null);
DataWatcherHelper.DataWatcherSerializer_FLOAT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("c").get(null);
DataWatcherHelper.DataWatcherSerializer_STRING = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("d").get(null);
if (getMinorVersion() >= 13) {
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("f").get(null);
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("i").get(null);
} else {
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("h").get(null);
}
} else {
DataWatcher.REGISTER = DataWatcher.CLASS.getMethod("a", int.class, Object.class);
}
if (minorVersion >= 13) {
PacketScoreboard.ScoreboardTeam_setPrefix = PacketScoreboard.ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
PacketScoreboard.ScoreboardTeam_setSuffix = PacketScoreboard.ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
} else {
PacketScoreboard.ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[] {"setPrefix", "b"}, String.class); // 1.5.1+, 1.5
PacketScoreboard.ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[] {"setSuffix", "c"}, String.class); // 1.5.1+, 1.5
try {
return getClass().getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
// nested class not found
throw new ClassNotFoundException(name);
}
}
@@ -7,7 +7,6 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.*;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and bukkit names.
@@ -15,37 +14,6 @@ import me.neznamy.tab.shared.util.ReflectionUtils;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitModernNMSStorage extends NMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
DataWatcher.REGISTER = ReflectionUtils.getMethod(DataWatcher.CLASS, new String[]{"register", "a"}, DataWatcherObject.CLASS, Object.class); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardScore_setScore = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardScoreClass, new String[]{"setScore", "b"}, int.class); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[]{"setPrefix", "b"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[]{"setSuffix", "c"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
PacketScoreboard.ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(PacketScoreboard.ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, PacketScoreboard.EnumNameTagVisibility); // {Bukkit, Bukkit 1.18+}
DataWatcherHelper.DataWatcherSerializer_BYTE = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("a").get(null);
DataWatcherHelper.DataWatcherSerializer_FLOAT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("c").get(null);
DataWatcherHelper.DataWatcherSerializer_STRING = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("d").get(null);
if (is1_19_3Plus()) {
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("g").get(null);
if (is1_19_4Plus()) {
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("k").get(null);
} else {
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("j").get(null);
}
} else {
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("f").get(null);
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("i").get(null);
}
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLivingStorage.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLivingStorage.EntityTypes.getDeclaredField("d").get(null);
(PacketPlayOutSpawnEntityLivingStorage.ENTITY_TYPE = PacketPlayOutSpawnEntityLivingStorage.CLASS.getDeclaredField("e")).setAccessible(true);
DataWatcher.packDirty = DataWatcher.CLASS.getMethod("b");
}
}
@Override
public void loadClasses() throws ClassNotFoundException {
ChatSerializer = Class.forName("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer");
@@ -86,12 +54,9 @@ public class BukkitModernNMSStorage extends NMSStorage {
PacketPlayOutEntityMetadataStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
BukkitTabList.EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLivingStorage.EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
} else {
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
}
PacketPlayOutSpawnEntityLivingStorage.Vec3D = Class.forName("net.minecraft.world.phys.Vec3D");
PacketPlayOutSpawnEntityLivingStorage.EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
if (is1_19_3Plus()) {
BukkitTabList.ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
BukkitTabList.PacketPlayOutPlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
@@ -1,97 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms.storage.nms;
import me.neznamy.tab.platforms.bukkit.BukkitTabList;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherHelper;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherObject;
import me.neznamy.tab.platforms.bukkit.nms.storage.packet.*;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and names.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class MojangModernNMSStorage extends NMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("fromJson", String.class);
DataWatcher.REGISTER = DataWatcher.CLASS.getMethod("define", DataWatcherObject.CLASS, Object.class);
PacketScoreboard.ScoreboardScore_setScore = PacketScoreboard.ScoreboardScoreClass.getMethod("setScore", int.class);
PacketScoreboard.ScoreboardTeam_setAllowFriendlyFire = PacketScoreboard.ScoreboardTeam.getMethod("setAllowFriendlyFire", boolean.class);
PacketScoreboard.ScoreboardTeam_setCanSeeFriendlyInvisibles = PacketScoreboard.ScoreboardTeam.getMethod("setSeeFriendlyInvisibles", boolean.class);
PacketScoreboard.ScoreboardTeam_setPrefix = PacketScoreboard.ScoreboardTeam.getMethod("setPlayerPrefix", IChatBaseComponent);
PacketScoreboard.ScoreboardTeam_setSuffix = PacketScoreboard.ScoreboardTeam.getMethod("setPlayerSuffix", IChatBaseComponent);
PacketScoreboard.ScoreboardTeam_setNameTagVisibility = PacketScoreboard.ScoreboardTeam.getMethod("setNameTagVisibility", PacketScoreboard.EnumNameTagVisibility);
DataWatcherHelper.DataWatcherSerializer_BYTE = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("BYTE").get(null);
DataWatcherHelper.DataWatcherSerializer_FLOAT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("FLOAT").get(null);
DataWatcherHelper.DataWatcherSerializer_STRING = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("STRING").get(null);
DataWatcherHelper.DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("OPTIONAL_COMPONENT").get(null);
DataWatcherHelper.DataWatcherSerializer_BOOLEAN = DataWatcherHelper.DataWatcherRegistry.getDeclaredField("BOOLEAN").get(null);
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLivingStorage.EntityTypes_ARMOR_STAND = PacketPlayOutSpawnEntityLivingStorage.EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
(PacketPlayOutSpawnEntityLivingStorage.ENTITY_TYPE = PacketPlayOutSpawnEntityLivingStorage.CLASS.getDeclaredField("type")).setAccessible(true);
DataWatcher.packDirty = DataWatcher.CLASS.getMethod("packDirty");
}
}
@Override
public void loadClasses() throws ClassNotFoundException {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
ChatSerializer = Class.forName("net.minecraft.network.chat.Component$Serializer");
World = Class.forName("net.minecraft.world.level.Level");
EntityArmorStand = Class.forName("net.minecraft.world.entity.decoration.ArmorStand");
EntityHuman = Class.forName("net.minecraft.world.entity.player.Player");
NetworkManager = Class.forName("net.minecraft.network.Connection");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
Entity = Class.forName("net.minecraft.world.entity.Entity");
EntityLiving = Class.forName("net.minecraft.world.entity.LivingEntity");
PlayerConnection = Class.forName("net.minecraft.server.network.ServerGamePacketListenerImpl");
BukkitTabList.PacketPlayOutPlayerListHeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
DataWatcher.CLASS = Class.forName("net.minecraft.network.syncher.SynchedEntityData");
DataWatcherItem.CLASS = Class.forName("net.minecraft.network.syncher.SynchedEntityData$DataItem");
DataWatcherObject.CLASS = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
DataWatcherHelper.DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
DataWatcherHelper.DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
PacketPlayOutEntityTeleportStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
PacketPlayOutEntity = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket");
PacketPlayOutEntityDestroyStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket");
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
PacketPlayOutEntityMetadataStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
BukkitTabList.EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
PacketScoreboard.DisplayObjectiveClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
PacketScoreboard.ObjectivePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
PacketScoreboard.Scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
PacketScoreboard.ScorePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetScorePacket");
PacketScoreboard.ScoreboardObjective = Class.forName("net.minecraft.world.scores.Objective");
PacketScoreboard.ScoreboardScoreClass = Class.forName("net.minecraft.world.scores.Score");
PacketScoreboard.IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria");
PacketScoreboard.EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
PacketScoreboard.EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ServerScoreboard$Method");
PacketScoreboard.TeamPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket");
PacketScoreboard.ScoreboardTeam = Class.forName("net.minecraft.world.scores.PlayerTeam");
PacketScoreboard.EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$Visibility");
PacketScoreboard.EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLivingStorage.EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
} else {
PacketPlayOutSpawnEntityLivingStorage.CLASS = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
}
if (is1_19_3Plus()) {
BukkitTabList.ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
BukkitTabList.PacketPlayOutPlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
BukkitTabList.EnumPlayerInfoActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action");
BukkitTabList.PlayerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry");
BukkitTabList.RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data");
} else {
BukkitTabList.PacketPlayOutPlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
BukkitTabList.EnumPlayerInfoActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
BukkitTabList.PlayerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
}
}
}
@@ -6,6 +6,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabList;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherHelper;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
@@ -97,8 +98,13 @@ public abstract class NMSStorage {
sendPacket = ReflectionUtils.getMethod(PlayerConnection, new String[]{"sendPacket"}, Packet);
}
if (minorVersion >= 8) {
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
getProfile = ReflectionUtils.getOnlyMethod(EntityHuman, GameProfile.class);
try {
getProfile = ReflectionUtils.getOnlyMethod(EntityHuman, GameProfile.class);
} catch (IllegalStateException catServer) {
getProfile = EntityHuman.getMethod("getProfile");
}
Constructor<?> newEntityArmorStand = EntityArmorStand.getConstructor(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);
@@ -111,6 +117,7 @@ public abstract class NMSStorage {
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
DataWatcher.load(this);
DataWatcherItem.load(this);
DataWatcherHelper.load(this);
PacketPlayOutEntityDestroyStorage.load();
PacketPlayOutEntityMetadataStorage.load(this);
PacketPlayOutEntityTeleportStorage.load(this);
@@ -118,7 +125,6 @@ public abstract class NMSStorage {
PacketScoreboard.load(this);
PacketPlayOutEntity_ENTITYID = ReflectionUtils.getFields(PacketPlayOutEntity, int.class).get(0);
PacketPlayOutNamedEntitySpawn_ENTITYID = ReflectionUtils.getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
loadNamedFieldsAndMethods();
if (minorVersion < 17) {
try {
(PING = EntityPlayer.getDeclaredField("ping")).setAccessible(true); // 1.5.2 - 1.16.5
@@ -136,15 +142,6 @@ public abstract class NMSStorage {
*/
public abstract void loadClasses() throws ClassNotFoundException;
/**
* Loads all fields and methods which must be loaded by their name due to
* lack of other methods of reliably retrieving them.
*
* @throws ReflectiveOperationException
* If a field or method was not found
*/
public abstract void loadNamedFieldsAndMethods() throws ReflectiveOperationException;
/**
* Converts TAB's IChatBaseComponent into minecraft's component using String deserialization.
* If the requested component is found in cache, it is returned. If not, it is created, added into cache and returned.
@@ -1,31 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms.storage.nms;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import org.jetbrains.annotations.NotNull;
/**
* NMS loader for Thermos 1.7.10.
*/
public class ThermosNMSStorage extends BukkitLegacyNMSStorage {
@Override
public Class<?> getLegacyClass(@NotNull String name) throws ClassNotFoundException {
try {
return getClass().getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
// nested class not found
throw new ClassNotFoundException(name);
}
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PacketScoreboard.ScoreboardScore_setScore = PacketScoreboard.ScoreboardScoreClass.getMethod("func_96647_c", int.class);
PacketScoreboard.ScoreboardTeam_setAllowFriendlyFire = PacketScoreboard.ScoreboardTeam.getMethod("func_96660_a", boolean.class);
PacketScoreboard.ScoreboardTeam_setCanSeeFriendlyInvisibles = PacketScoreboard.ScoreboardTeam.getMethod("func_98300_b", boolean.class);
DataWatcher.REGISTER = DataWatcher.CLASS.getMethod("func_75682_a", int.class, Object.class);
PacketScoreboard.ScoreboardTeam_setPrefix = PacketScoreboard.ScoreboardTeam.getMethod("func_96666_b", String.class);
PacketScoreboard.ScoreboardTeam_setSuffix = PacketScoreboard.ScoreboardTeam.getMethod("func_96662_c", String.class);
}
}
@@ -20,8 +20,15 @@ public class PacketPlayOutSpawnEntityLivingStorage {
/** NMS Fields */
public static Class<?> CLASS;
public static Class<?> EntityTypes;
public static Constructor<?> CONSTRUCTOR;
/** 1.17+ */
public static Class<?> Vec3D;
public static Class<?> EntityTypes;
public static Object Vec3D_Empty;
public static Object EntityTypes_ARMOR_STAND;
/** 1.16.5- */
public static Field ENTITY_ID;
public static Field ENTITY_TYPE;
public static Field YAW;
@@ -31,9 +38,6 @@ public class PacketPlayOutSpawnEntityLivingStorage {
public static Field Y;
public static Field Z;
public static Field DATA_WATCHER;
public static Object EntityTypes_ARMOR_STAND;
/** Entity types */
public static final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
/**
@@ -44,47 +48,44 @@ public class PacketPlayOutSpawnEntityLivingStorage {
* @throws NoSuchMethodException
* If something fails
*/
public static void load(NMSStorage nms) throws NoSuchMethodException {
if (nms.getMinorVersion() >= 19) {
entityIds.put(EntityType.ARMOR_STAND, 2);
} else if (nms.getMinorVersion() >= 13) {
public static void load(NMSStorage nms) throws NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
int minorVersion = nms.getMinorVersion();
if (minorVersion >= 13) {
entityIds.put(EntityType.ARMOR_STAND, 1);
} else {
entityIds.put(EntityType.WITHER, 64);
if (nms.getMinorVersion() >= 8) {
if (minorVersion >= 8) {
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
if (nms.getMinorVersion() >= 17) {
CONSTRUCTOR = CLASS.getConstructor(nms.is1_19_3Plus() ? nms.Entity : nms.EntityLiving);
if (minorVersion >= 19) {
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("d").get(null);
CONSTRUCTOR = CLASS.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D, double.class);
Vec3D_Empty = ReflectionUtils.getOnlyField(Vec3D, Vec3D).get(null);
} else if (minorVersion >= 17) {
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("c").get(null);
CONSTRUCTOR = CLASS.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D);
Vec3D_Empty = ReflectionUtils.getOnlyField(Vec3D, Vec3D).get(null);
} else {
CONSTRUCTOR = CLASS.getConstructor();
}
ENTITY_ID = ReflectionUtils.getFields(CLASS, int.class).get(0);
YAW = ReflectionUtils.getFields(CLASS, byte.class).get(0);
PITCH = ReflectionUtils.getFields(CLASS, byte.class).get(1);
if (nms.getMinorVersion() >= 9) {
UUID = ReflectionUtils.getOnlyField(CLASS, UUID.class);
if (nms.getMinorVersion() >= 19) {
X = ReflectionUtils.getFields(CLASS, double.class).get(2);
Y = ReflectionUtils.getFields(CLASS, double.class).get(3);
Z = ReflectionUtils.getFields(CLASS, double.class).get(4);
} else {
ENTITY_ID = ReflectionUtils.getFields(CLASS, int.class).get(0);
YAW = ReflectionUtils.getFields(CLASS, byte.class).get(0);
PITCH = ReflectionUtils.getFields(CLASS, byte.class).get(1);
if (minorVersion >= 9) {
UUID = ReflectionUtils.getOnlyField(CLASS, UUID.class);
X = ReflectionUtils.getFields(CLASS, double.class).get(0);
Y = ReflectionUtils.getFields(CLASS, double.class).get(1);
Z = ReflectionUtils.getFields(CLASS, double.class).get(2);
} else {
X = ReflectionUtils.getFields(CLASS, int.class).get(2);
Y = ReflectionUtils.getFields(CLASS, int.class).get(3);
Z = ReflectionUtils.getFields(CLASS, int.class).get(4);
}
} else {
X = ReflectionUtils.getFields(CLASS, int.class).get(2);
Y = ReflectionUtils.getFields(CLASS, int.class).get(3);
Z = ReflectionUtils.getFields(CLASS, int.class).get(4);
}
if (nms.getMinorVersion() < 19) {
ENTITY_TYPE = ReflectionUtils.getFields(CLASS, int.class).get(1);
}
if (nms.getMinorVersion() <= 14) {
DATA_WATCHER = ReflectionUtils.getOnlyField(CLASS, DataWatcher.CLASS);
if (minorVersion <= 14) {
DATA_WATCHER = ReflectionUtils.getOnlyField(CLASS, DataWatcher.CLASS);
}
}
}
@@ -94,35 +95,32 @@ public class PacketPlayOutSpawnEntityLivingStorage {
* @return NMS packet
*/
@SneakyThrows
public static Object build(int entityId, UUID id, Object entityType, Location location, EntityData data) {
NMSStorage nms = NMSStorage.getInstance();
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = CONSTRUCTOR.newInstance(nms.dummyEntity);
} else {
nmsPacket = CONSTRUCTOR.newInstance();
public static Object build(int id, UUID uuid, Object entityType, Location l, EntityData data) {
int minorVersion = NMSStorage.getInstance().getMinorVersion();
if (minorVersion >= 19) {
return CONSTRUCTOR.newInstance(id, uuid, l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch(), EntityTypes_ARMOR_STAND, 0, Vec3D_Empty, 0d);
}
ENTITY_ID.set(nmsPacket, entityId);
YAW.set(nmsPacket, (byte)(location.getYaw() * 256.0f / 360.0f));
PITCH.set(nmsPacket, (byte)(location.getPitch() * 256.0f / 360.0f));
if (nms.getMinorVersion() <= 14) {
if (minorVersion >= 17) {
return CONSTRUCTOR.newInstance(id, uuid, l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch(), EntityTypes_ARMOR_STAND, 0, Vec3D_Empty);
}
Object nmsPacket = CONSTRUCTOR.newInstance();
ENTITY_ID.set(nmsPacket, id);
YAW.set(nmsPacket, (byte)(l.getYaw() * 256.0f / 360.0f));
PITCH.set(nmsPacket, (byte)(l.getPitch() * 256.0f / 360.0f));
if (minorVersion <= 14) {
DATA_WATCHER.set(nmsPacket, data.build());
}
if (nms.getMinorVersion() >= 9) {
UUID.set(nmsPacket, id);
X.set(nmsPacket, location.getX());
Y.set(nmsPacket, location.getY());
Z.set(nmsPacket, location.getZ());
if (minorVersion >= 9) {
UUID.set(nmsPacket, uuid);
X.set(nmsPacket, l.getX());
Y.set(nmsPacket, l.getY());
Z.set(nmsPacket, l.getZ());
} else {
X.set(nmsPacket, floor(location.getX()*32));
Y.set(nmsPacket, floor(location.getY()*32));
Z.set(nmsPacket, floor(location.getZ()*32));
}
if (nms.getMinorVersion() >= 19) {
ENTITY_TYPE.set(nmsPacket, EntityTypes_ARMOR_STAND); // :(
} else {
ENTITY_TYPE.set(nmsPacket, entityIds.get((EntityType) entityType));
X.set(nmsPacket, floor(l.getX()*32));
Y.set(nmsPacket, floor(l.getY()*32));
Z.set(nmsPacket, floor(l.getZ()*32));
}
ENTITY_TYPE.set(nmsPacket, entityIds.get((EntityType) entityType));
return nmsPacket;
}
@@ -1,10 +1,13 @@
package me.neznamy.tab.platforms.bukkit;
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.platforms.bukkit.BukkitPipelineInjector;
import me.neznamy.tab.platforms.bukkit.BukkitPlaceholderRegistry;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.GroupManager;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
@@ -28,6 +31,7 @@ import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
@@ -118,20 +122,7 @@ public class BukkitPlatform implements BackendPlatform {
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, pl.getRefreshInterval(identifier), (viewer, target) ->
placeholderAPI ? PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier) : identifier);
} else if (identifier.startsWith("%sync:")) {
String syncedPlaceholder = "%" + identifier.substring(6, identifier.length()-1) + "%";
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = pl.registerPlayerPlaceholder(identifier, refresh, p -> {
try {
Bukkit.getScheduler().runTask(plugin, () -> {
long time = System.nanoTime();
ppl[0].updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime()-time);
});
return null;
} catch (UnsupportedOperationException e) {
return "<Folia does not support sync placeholders>";
}
});
registerSyncPlaceholder(identifier, refresh);
} else if (identifier.startsWith("%server_")) {
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, refresh, () ->
placeholderAPI ? PlaceholderAPI.setPlaceholders(null, identifier) : identifier);
@@ -141,6 +132,19 @@ public class BukkitPlatform implements BackendPlatform {
}
}
public void registerSyncPlaceholder(String identifier, int refresh) {
String syncedPlaceholder = "%" + identifier.substring(6);
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p -> {
Bukkit.getScheduler().runTask(plugin, () -> {
long time = System.nanoTime();
ppl[0].updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime()-time);
});
return null;
});
}
@Override
public void sendConsoleMessage(@NotNull IChatBaseComponent message) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + RGBUtils.getInstance().convertToBukkitFormat(message.toFlatText(),
@@ -165,4 +169,8 @@ public class BukkitPlatform implements BackendPlatform {
}
return new GroupManager("None", p -> TabConstants.NO_GROUP);
}
public void runEntityTask(Entity entity, Runnable task) {
task.run();
}
}
@@ -0,0 +1,88 @@
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.function.Consumer;
/**
* Platform override for Folia
*/
public class FoliaPlatform extends BukkitPlatform {
public FoliaPlatform(JavaPlugin plugin) {
super(plugin);
}
@Override
public void loadPlayers() {
super.loadPlayers();
// Values are never updated in the API, warn users
TAB.getInstance().sendConsoleMessage("&cFolia never updates MSPT and TPS values in the API, making " +
"%mspt% and %tps% return the default values (0 and 20).", true);
// Folia never calls PlayerChangedWorldEvent, this is a workaround
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(100, "Folia compatibility", "Refreshing world", () -> {
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
String bukkitWorld = ((Player)player.getPlayer()).getWorld().getName();
if (!player.getWorld().equals(bukkitWorld)) {
TAB.getInstance().getFeatureManager().onWorldChange(player.getUniqueId(), bukkitWorld);
PerWorldPlayerList pwp = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST);
if (pwp != null) {
runSync((Entity) player.getPlayer(), () -> pwp.onWorldChange(new PlayerChangedWorldEvent((Player) player.getPlayer(), ((Player) player.getPlayer()).getWorld())));
}
}
}
});
}
@Override
public void registerSyncPlaceholder(String identifier, int refresh) {
String syncedPlaceholder = "%" + identifier.substring(6);
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p -> {
runSync((Entity) p.getPlayer(), () -> {
long time = System.nanoTime();
String output = isPlaceholderAPI() ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier;
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime()-time);
TAB.getInstance().getCPUManager().runTask(() -> ppl[0].updateValue(p, output)); // To ensure player is loaded
});
return null;
});
}
/**
* Runs task using player's entity scheduler. It's using reflection, because
* Folia uses Java 17 while TAB maintains Java 8 compatibility for compatibility
* with MC versions older than their player base.
*
* @param entity
* entity to run task for
* @param task
* Task to run
*/
@SneakyThrows
@SuppressWarnings("JavaReflectionMemberAccess")
private void runSync(Entity entity, Runnable task) {
Object entityScheduler = Entity.class.getMethod("getScheduler").invoke(entity);
Consumer<?> consumer = $ -> task.run(); // Reflection and lambdas don't go together
entityScheduler.getClass().getMethod("run", Plugin.class, Consumer.class, Runnable.class)
.invoke(entityScheduler, getPlugin(), consumer, null);
}
@Override
public void runEntityTask(Entity entity, Runnable task) {
runSync(entity, task);
}
}
@@ -115,15 +115,22 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
TeamPacket_ACTION = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class).get(0);
TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(TeamPacketClass, Collection.class);
ScoreboardTeam_getPlayerNameSet = ReflectionUtils.getOnlyMethod(ScoreboardTeam, Collection.class);
ScoreboardScore_setScore = ReflectionUtils.getMethod(ScoreboardScoreClass, new String[] {"func_96647_c", "setScore", "b", "c"}, int.class); // {Thermos, 1.5.1 - 1.17.1, 1.18+, 1.5}
ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(ScoreboardTeam, new String[] {"func_96660_a", "setAllowFriendlyFire", "a"}, boolean.class); // {Thermos, 1.5.1+, 1.5 & 1.18+}
ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(ScoreboardTeam, new String[] {"func_98300_b", "setCanSeeFriendlyInvisibles", "b"}, boolean.class); // {Thermos, 1.5.1+, 1.5 & 1.18+}
if (nms.getMinorVersion() >= 13) {
newScorePacket_1_13 = ScorePacketClass.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
newObjectivePacket = ObjectivePacketClass.getConstructor(ScoreboardObjective, int.class);
Objective_DISPLAY_NAME = ReflectionUtils.getOnlyField(ObjectivePacketClass, nms.IChatBaseComponent);
ScoreboardTeam_setColor = ReflectionUtils.getOnlyMethod(ScoreboardTeam, void.class, nms.EnumChatFormat);
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(ScoreboardTeam, new String[]{"setPrefix", "b"}, nms.IChatBaseComponent); // {1.17.1-, 1.18+}
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(ScoreboardTeam, new String[]{"setSuffix", "c"}, nms.IChatBaseComponent); // {1.17.1-, 1.18+}
} else {
newScorePacket_String = ScorePacketClass.getConstructor(String.class);
newObjectivePacket = ObjectivePacketClass.getConstructor();
Objective_DISPLAY_NAME = ReflectionUtils.getFields(ObjectivePacketClass, String.class).get(1);
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(ScoreboardTeam, new String[] {"func_96666_b", "setPrefix", "b"}, String.class); // {Thermos, 1.5.1+, 1.5}
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(ScoreboardTeam, new String[] {"func_96662_c", "setSuffix", "c"}, String.class); // {Thermos, 1.5.1+, 1.5}
if (nms.getMinorVersion() >= 8) {
newScorePacket = ScorePacketClass.getConstructor(ScoreboardScoreClass);
Objective_RENDER_TYPE = ReflectionUtils.getOnlyField(ObjectivePacketClass, EnumScoreboardHealthDisplay);
@@ -131,6 +138,9 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
newScorePacket = ScorePacketClass.getConstructor(ScoreboardScoreClass, int.class);
}
}
if (nms.getMinorVersion() >= 8) {
ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(ScoreboardTeam, new String[] {"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {1.8.1+, 1.8 & 1.18+}
}
if (nms.getMinorVersion() >= 9) {
ScoreboardTeam_setCollisionRule = ReflectionUtils.getOnlyMethod(ScoreboardTeam, void.class, EnumTeamPush);
}
@@ -2,10 +2,12 @@ package me.neznamy.tab.platforms.bukkit.scoreboard;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Team;
@@ -27,7 +29,9 @@ import java.util.Collection;
* changes and fields fail to load. Or as a complete replacement
* if compatibility with other plugins turns out not to be a problem.
*/
@SuppressWarnings("ConstantConditions") // Throw the NPE if something is not as expected, parent class should ensure it anyway
// Throw the NPE if something is not as expected, parent class should ensure it anyway
// Use Bukkit setColor, the adventure one does not support magic codes while they are permitted
@SuppressWarnings({"ConstantConditions", "deprecation"})
public class PaperScoreboard extends Scoreboard<BukkitTabPlayer> {
private final org.bukkit.scoreboard.Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
@@ -85,6 +89,7 @@ public class PaperScoreboard extends Scoreboard<BukkitTabPlayer> {
Team team = scoreboard.registerNewTeam(name);
team.prefix(toAdventure(prefix));
team.suffix(toAdventure(suffix));
team.setColor(ChatColor.valueOf(EnumChatFormat.lastColorsOf(prefix).name()));
team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.values()[visibility.ordinal()]);
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.values()[collision.ordinal()]);
players.forEach(team::addEntry);
@@ -105,6 +110,7 @@ public class PaperScoreboard extends Scoreboard<BukkitTabPlayer> {
Team team = scoreboard.getTeam(name);
team.prefix(toAdventure(prefix));
team.suffix(toAdventure(suffix));
team.setColor(ChatColor.valueOf(EnumChatFormat.lastColorsOf(prefix).name()));
team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.values()[visibility.ordinal()]);
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.values()[collision.ordinal()]);
team.setAllowFriendlyFire((options & 0x01) != 0);
+1 -1
View File
@@ -11,7 +11,7 @@ softdepend: [PlaceholderAPI, LuckPerms, LibsDisguises, ViaVersion, ViaRewind, fl
loadbefore: []
prefix: TAB
libraries: []
folia-supported: false
folia-supported: true
commands:
tab:
description: Plugin's main command
@@ -54,11 +54,20 @@ public class CpuManager {
for (Entry<String, Long> entry : placeholderUsagePrevious.entrySet()) {
float usagePercent = nanosToPercent(entry.getValue());
if (usagePercent > 50) {
if (usagePercent > 30) {
TAB.getInstance().sendConsoleMessage("&c[WARN] CPU usage of placeholder " + entry.getKey() +
" is " + (int)usagePercent + "%. It will most likely cause problems. Try increasing refresh interval.", true);
}
}
Map<String, Map<String, Float>> features = getFeatureUsage();
double featuresTotal = 0;
for (Map<String, Float> map : features.values()) {
featuresTotal += map.values().stream().mapToDouble(Float::floatValue).sum();
}
if (featuresTotal > 90) {
TAB.getInstance().sendConsoleMessage("&c[WARN] CPU usage of the plugin is "
+ (int)featuresTotal + "%. This will cause problems. Check /tab cpu to find out why.", true);
}
});
}
@@ -306,6 +306,9 @@ public class FeatureManager {
if (configuration.getConfig().getBoolean("per-world-playerlist.enabled", false)) {
TabFeature pwp = TAB.getInstance().getPlatform().getPerWorldPlayerList();
if (pwp != null) featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, pwp);
if (configuration.getConfig().getBoolean("layout.enabled", false)) {
TAB.getInstance().getMisconfigurationHelper().bothPerWorldPlayerListAndLayoutEnabled();
}
}
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
@@ -187,6 +187,13 @@ public class MisconfigurationHelper {
"Disable global playerlist for the same result, but with better performance.");
}
public void bothPerWorldPlayerListAndLayoutEnabled() {
startupWarn("Both per world playerlist and layout features are enabled, but layout makes per world playerlist redundant.",
"Layout automatically works with all connected players and replaces real player entries with" +
" fake players, making per world playerlist completely useless as real players are pushed out of the playerlist.",
"Disable per world playerlist for the same result, but with better performance.");
}
public void checkLayoutMap(@NotNull String layoutName, @NotNull Map<String, Object> map) {
List<String> expectedKeys = Arrays.asList("condition", "fixed-slots", "groups");
for (String mapKey : map.keySet()) {
@@ -202,4 +202,6 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
public abstract double getZ(@NotNull TabPlayer player);
public abstract EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible);
public abstract void runInEntityScheduler(Object entity, Runnable task);
}
@@ -62,7 +62,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
Object vehicle = feature.getVehicle(p);
if (vehicle != null) {
vehicles.put(feature.getEntityId(vehicle), feature.getPassengers(vehicle));
updateVehicle(vehicle);
playersInVehicle.put(p, vehicle);
if (feature.isDisableOnBoats() && feature.getEntityType(vehicle).contains("boat")) {
playersOnBoats.add(p);
@@ -74,7 +74,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
@Override
public void onJoin(@NotNull TabPlayer connectedPlayer) {
Object vehicle = feature.getVehicle(connectedPlayer);
if (vehicle != null) vehicles.put(feature.getEntityId(vehicle), feature.getPassengers(vehicle));
if (vehicle != null) updateVehicle(vehicle);
}
@Override
@@ -102,7 +102,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
}
if (!playersInVehicle.containsKey(p) && vehicle != null) {
//vehicle enter
vehicles.put(feature.getEntityId(vehicle), feature.getPassengers(vehicle));
updateVehicle(vehicle);
feature.getArmorStandManager(p).respawn(); //making teleport instant instead of showing teleport animation
playersInVehicle.put(p, vehicle);
if (feature.isDisableOnBoats() && feature.getEntityType(vehicle).contains("boat")) {
@@ -123,4 +123,8 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
public boolean isOnBoat(@NotNull TabPlayer p) {
return playersOnBoats.contains(p);
}
private void updateVehicle(Object vehicle) {
feature.runInEntityScheduler(vehicle, () -> vehicles.put(feature.getEntityId(vehicle), feature.getPassengers(vehicle)));
}
}
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Map;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.config.file.ConfigurationFile;
@@ -66,6 +67,11 @@ public class DebugCommand extends SubCommand {
sendMessage(sender, "&6Storage type: &b" + (tab.getConfiguration().getGroups() instanceof ConfigurationFile ? "File" : "MySQL"));
sendMessage(sender, separator);
if (analyzed == null) return;
if (!analyzed.isLoaded()) {
sendMessage(sender, "&cThe specified player is not loaded. This is either because player failed to load" +
" due to an error (see TAB's folder for errors.log file) or the plugin is overloaded (see /tab cpu).");
return;
}
sendMessage(sender, "&ePlayer: &a" + analyzed.getName());
if (analyzed instanceof ProxyTabPlayer) {
sendMessage(sender, "&eBridge connection: " + (((ProxyTabPlayer)analyzed).isBridgeConnected() ? "&aConnected" : "&cNot connected"));
@@ -88,8 +94,12 @@ public class DebugCommand extends SubCommand {
boolean disabledNametags = ((NameTag) tab.getNameTagManager()).getDisableChecker().isDisabledPlayer(analyzed);
showProperty(sender, analyzed, TabConstants.Property.TAGPREFIX, disabledNametags);
showProperty(sender, analyzed, TabConstants.Property.TAGSUFFIX, disabledNametags);
for (String line : getExtraLines()) {
showProperty(sender, analyzed, line, disabledNametags);
NameTagX nameTagX = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
if (nameTagX != null) {
boolean disabledUnlimited = nameTagX.isPlayerDisabled(analyzed);
for (String line : getExtraLines()) {
showProperty(sender, analyzed, line, disabledUnlimited);
}
}
} else {
sendMessage(sender, "&atagprefix: &cDisabled");
@@ -44,6 +44,10 @@ public class ParseCommand extends SubCommand {
}
}
String replaced = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
if (!replaced.contains("%")) {
sendMessage(sender, "&cThe provided input (" + replaced + ") does not contain any placeholders, therefore there's nothing to test.");
return;
}
String message = EnumChatFormat.color("&6Replacing placeholder &e%placeholder% &6for player &e" + target.getName()).replace("%placeholder%", replaced);
sendRawMessage(sender, message);
try {
@@ -26,24 +26,33 @@ public class FixedSlot extends TabFeature implements Refreshable {
private final UUID id;
private final String text;
private final String propertyName;
private final TabList.Skin skin;
private final String skin;
private final String skinProperty;
private final int ping;
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
if (!manager.getViews().containsKey(p) || manager.getViews().get(p).getPattern() != pattern || p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
p.getTabList().updateDisplayName(id, IChatBaseComponent.optimizedComponent(p.getProperty(propertyName).updateAndGet()));
boolean updateName = p.getProperty(propertyName).update(); // update property so skin change can use new name, avoids sending both packets
if (p.getProperty(skinProperty).update()) {
p.getTabList().removeEntry(id);
p.getTabList().addEntry(createEntry(p));
return;
}
if (updateName) p.getTabList().updateDisplayName(id, IChatBaseComponent.optimizedComponent(p.getProperty(propertyName).get()));
}
public @NotNull TabList.Entry createEntry(@NotNull TabPlayer viewer) {
viewer.setProperty(this, propertyName, text);
viewer.setProperty(this, skinProperty, skin);
return new TabList.Entry(
id,
manager.getDirection().getEntryName(viewer, slot),
skin,
manager.getSkinManager().getSkin(viewer.getProperty(skinProperty).get()),
ping,
0,
IChatBaseComponent.optimizedComponent(viewer.getProperty(propertyName).updateAndGet()) // maybe just get is fine?
IChatBaseComponent.optimizedComponent(viewer.getProperty(propertyName).get())
);
}
@@ -69,11 +78,12 @@ public class FixedSlot extends TabFeature implements Refreshable {
pattern,
manager.getUUID(slot),
text,
"Layout-" + line + "-SLOT-" + slot,
manager.getSkinManager().getSkin(skin.length() == 0 ? manager.getDefaultSkin() : skin),
"Layout-" + pattern.getName() + "-SLOT-" + slot,
skin.length() == 0 ? manager.getDefaultSkin() : skin,
"Layout-" + pattern.getName() + "-SLOT-" + slot + "-skin",
ping
);
if (text.length() > 0) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layoutSlot(line, slot), f);
if (text.length() > 0) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layoutSlot(pattern.getName(), slot), f);
return f;
}
}
@@ -93,7 +93,8 @@ public class LayoutPattern extends TabFeature implements Refreshable, Layout {
@Override
public void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping) {
fixedSlots.put(slot, new FixedSlot(manager, slot, this, manager.getUUID(slot), text,
"Layout-" + text + "-SLOT-" + slot, manager.getSkinManager().getSkin(skin), ping));
"Layout-" + text + "-SLOT-" + slot, skin, "Layout-" + text + "-SLOT-" + slot+ "-skin", ping));
}
@Override
@@ -56,7 +56,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
updateProperties(all);
armorStandManagerMap.put(all, armorStandFunction.apply(this, all));
if (unlimitedDisableChecker.isDisableConditionMet(all)) {
unlimitedDisableChecker.addDisabledPlayer(all);
addDisabledPlayer(all);
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(all, false);
}
@@ -66,12 +66,16 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public void onJoin(@NotNull TabPlayer connectedPlayer) {
if (unlimitedDisableChecker.isDisableConditionMet(connectedPlayer))
unlimitedDisableChecker.addDisabledPlayer(connectedPlayer);
addDisabledPlayer(connectedPlayer);
super.onJoin(connectedPlayer);
armorStandManagerMap.put(connectedPlayer, armorStandFunction.apply(this, connectedPlayer));
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(connectedPlayer, false);
}
public void addDisabledPlayer(@NotNull TabPlayer player) {
unlimitedDisableChecker.addDisabledPlayer(player);
}
@Override
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
super.refresh(refreshed, force);
@@ -35,14 +35,16 @@ public class ViaVersionHook {
int version = Via.getAPI().getPlayerVersion(player);
if (version == -1) {
TAB.getInstance().sendConsoleMessage("&cViaVersion returned protocol version -1 for player " + playerName +
". Using version of the server (" + TAB.getInstance().getServerVersion().getNetworkId() + ")", true);
". Using server's version (" + TAB.getInstance().getServerVersion().getNetworkId() + " - " +
TAB.getInstance().getServerVersion().getFriendlyName() + ")", true);
return TAB.getInstance().getServerVersion().getNetworkId();
}
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
ProtocolVersion protocol = ProtocolVersion.fromNetworkId(version);
if (protocol == ProtocolVersion.UNKNOWN_CLIENT_VERSION) {
TAB.getInstance().sendConsoleMessage("&cViaVersion returned unknown protocol version " + version +
" for player " + playerName + ". This may result in plugin not working correctly for them.", true);
} else {
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
}
return version;
}
@@ -40,6 +40,7 @@ public class ServerPlaceholderImpl extends TabPlaceholder implements ServerPlace
if (identifier.startsWith("%rel_")) throw new IllegalArgumentException("\"rel_\" is reserved for relational placeholder identifiers");
this.supplier = supplier;
update0();
if (lastValue == null) lastValue = identifier;
}
/**
@@ -11,6 +11,7 @@ import me.neznamy.tab.shared.placeholders.conditions.Condition;
import org.jetbrains.annotations.NotNull;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
@@ -22,7 +23,13 @@ import java.util.Map.Entry;
public class UniversalPlaceholderRegistry implements PlaceholderRegistry {
/** Decimal formatter for 2 decimal places */
private final DecimalFormat decimal2 = new DecimalFormat("#.##");
private final DecimalFormat decimal2;
public UniversalPlaceholderRegistry() {
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator('.');
decimal2 = new DecimalFormat("#.##", symbols);
}
@SuppressWarnings("unchecked")
@Override
@@ -39,6 +39,12 @@ public class ProxyNameTagX extends NameTagX {
((ProxyTabPlayer)p).sendPluginMessage("NameTagX", "SetEnabled", !disabledNow && !getDisableChecker().isDisabledPlayer(p));
}
@Override
public void addDisabledPlayer(@NotNull TabPlayer player) {
super.addDisabledPlayer(player);
((ProxyTabPlayer)player).sendPluginMessage("NameTagX", "SetEnabled", false);
}
@Override
public void onDisableConditionChange(TabPlayer p, boolean disabledNow) {
super.onDisableConditionChange(p, disabledNow);
@@ -19,12 +19,7 @@ import org.slf4j.Logger;
import java.nio.file.Path;
/**
* Main class for Velocity platform.
* The velocity-plugin.json is manually deleted from releases and therefore
* requires manual compilation. This avoids unnecessary complications
* and bug reports from an unsupported platform.
* Most of the reasons why Velocity is not and will not be supported are listed at
* https://gist.github.com/NEZNAMY/21c1aabe57a0a462ee175386c510fdf8
* Main class for Velocity.
*/
@Plugin(
id = TabConstants.PLUGIN_ID,
@@ -64,6 +59,9 @@ public class VelocityTAB {
server.getChannelRegistrar().register(minecraftChannelIdentifier);
server.getEventManager().register(this, new VelocityEventListener());
TAB.setInstance(new TAB(new VelocityPlatform(this, server), ProtocolVersion.PROXY, dataFolder.toFile()));
TAB.getInstance().sendConsoleMessage("&c[WARN] Velocity compatibility is very experimental and should not be used in production! " +
"If you use it, you WILL run into issues and they WILL NOT be fixed. Any bug reports featuring Velocity installation " +
"will be immediately closed.", true);
TAB.getInstance().load();
metricsFactory.make(this, 10533).addCustomChart(new SimplePie(TabConstants.MetricsChart.GLOBAL_PLAYER_LIST_ENABLED,
() -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));