mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-12 20:22:01 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a748a6b2c0 | ||
|
|
5f8eb14c92 | ||
|
|
1fc5034a77 | ||
|
|
5130d1659e | ||
|
|
f68cba21f0 | ||
|
|
6c7d1eba32 | ||
|
|
9713db862a | ||
|
|
fbee22d785 | ||
|
|
0ee39728e5 | ||
|
|
37aa69b64e | ||
|
|
22ddad85ab | ||
|
|
261a81586f | ||
|
|
f0abc99b61 | ||
|
|
b669871ff1 | ||
|
|
174c58b642 | ||
|
|
28431ef136 | ||
|
|
5f25bf97ba | ||
|
|
f6359d2f77 | ||
|
|
727c500b17 | ||
|
|
bebf2988fb | ||
|
|
e3cde1968b | ||
|
|
ef6b668c70 | ||
|
|
fef62e796b | ||
|
|
0d4b1dbce2 | ||
|
|
43d210dbf8 | ||
|
|
e0d2209e97 | ||
|
|
720cae3e94 | ||
|
|
f2ea34a717 | ||
|
|
2c33747e1b | ||
|
|
5f9d72eb90 | ||
|
|
3abcc1d96f | ||
|
|
7d458422c7 | ||
|
|
0feee14bfb | ||
|
|
0023cfcafb |
No files matched your search
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.1.2"><title>Release: 6.1.2</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="275" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">Release</text><text x="275" y="150" fill-opacity=".3" textLength="430">Release</text></g><text x="275" y="140" textLength="430">Release</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="715" y="150" fill-opacity=".8" filter="url(#blur)" textLength="290">6.1.2</text><text x="715" y="150" fill-opacity=".3" textLength="290">6.1.2</text></g><text x="715" y="140" textLength="290">6.1.2</text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.2.0"><title>Release: 6.2.0</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="275" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">Release</text><text x="275" y="150" fill-opacity=".3" textLength="430">Release</text></g><text x="275" y="140" textLength="430">Release</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="715" y="150" fill-opacity=".8" filter="url(#blur)" textLength="290">6.2.0</text><text x="715" y="150" fill-opacity=".3" textLength="290">6.2.0</text></g><text x="715" y="140" textLength="290">6.2.0</text></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -274,7 +274,7 @@ if __name__ == "__main__":
|
||||
]
|
||||
)
|
||||
|
||||
release = "6.1.2"
|
||||
release = "6.2.0"
|
||||
download_image(f"https://img.shields.io/badge/Release-{release}-blue.svg", "Badge_Release.svg")
|
||||
|
||||
java = "8+"
|
||||
|
||||
@@ -13,7 +13,7 @@ TAB does not depend on any other plugins. The experience can however be enhanced
|
||||
With the introduction of new Minecraft versions, it is becoming harder and harder to support many versions in the same jar, eventually making it impossible since Minecraft 26.1.
|
||||
**As a general rule, if you want the latest TAB version for your server, please use [Modrinth](https://modrinth.com/plugin/tab-was-taken)'s download filter, where you can select your server software and version and you'll get the latest jar.**
|
||||
|
||||
Currently, the latest Minecraft version supported by TAB is **26.2**. When new versions come out, TAB is always updated soon after to support them.
|
||||
Currently, the latest Minecraft version supported by TAB is **26.3**. When new versions come out, TAB is always updated soon after to support them.
|
||||
|
||||
If your server software and version is reasonably popular and TAB hasn't been backported to that version in a very long time, you may [request a backport](https://github.com/NEZNAMY/TAB/issues/new/choose) (unless backports to that version are no longer offered, see below).
|
||||
Since TAB 6.0.0, Minecraft 1.x started taking hits with each subsequent TAB versions - Bukkit versions started disappearing and **modded platforms no longer offer backports of TAB 6.0.0+ to MC 1.x** (this is because the build scripts had to be massively changed for MC 26+).
|
||||
@@ -21,8 +21,17 @@ Since TAB 6.0.0, Minecraft 1.x started taking hits with each subsequent TAB vers
|
||||
The sections below provide **additional** information for each platform.
|
||||
|
||||
## Bukkit
|
||||
TAB 5.5.0 supports all 1.x versions starting with 1.7. For 1.5.2 - 1.6.4, use TAB v5.2.5.
|
||||
Since TAB 6.0.0, support for unused 1.x versions will continue to disappear with each subsequent Minecraft release.
|
||||
Since TAB 6.0.0, support for unused 1.x versions started disappearing and will continue to disappear with each subsequent Minecraft release.
|
||||
Currently, the latest release supports the following versions:
|
||||
* 1.7.10
|
||||
* 1.8.8
|
||||
* 1.12.2
|
||||
* 1.16.5
|
||||
* 1.17.1
|
||||
* 1.18.2
|
||||
* 1.19.4 - 26.3
|
||||
|
||||
If you want a jar with support for a 1.x version that is no longer supported, you can use TAB v5.5.0, which supports all MC 1.x versions. For 1.5.2 - 1.6.4, use TAB v5.2.5.
|
||||
|
||||
## BungeeCord / Velocity
|
||||
For proxies, the plugin is made to work with the latest build.
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
* ~Examples~
|
||||
|
||||
# About
|
||||
This feature allows you to see players from other servers, instead of only seeing players on the same server.
|
||||
If TAB is installed on a backend server, you'll need to set up [Multi server support](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Multi-server-support) for proper linking of servers. On proxy installation, this is not required.
|
||||
This feature allows you to see players from different servers, instead of only seeing players on the same server. The typical use is to show players under the whole proxy network.
|
||||
If TAB is installed on a backend server, you'll need to set up [Multi server support](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Multi-server-support) to properly make the servers communicate. On proxy installation, this is not needed, unless you have multiple proxies.
|
||||
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **global-playerlist** section.
|
||||
@@ -118,6 +118,8 @@ global-playerlist:
|
||||
- isolatedServer2
|
||||
```
|
||||
|
||||
This is not needed when `isolate-unlisted-servers` is set to `true`, in which case all unlisted servers are isolated by default.
|
||||
|
||||
## Seeing all players on the network from some server
|
||||
If, despite configuring server groups,
|
||||
you want players to see everyone on the network if they're connected to specific server(s),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* [Additional note 2 - [1.8 - 1.21.3] Second layer of skin missing](#additional-note-2---18---1213-second-layer-of-skin-missing)
|
||||
* [Additional note 3 - Entry overlap](#additional-note-3---entry-overlap)
|
||||
* ~Troubleshooting~
|
||||
* ~API~
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-server columns](#example-1---per-server-columns)
|
||||
* [Example 2 - Per-world playerlist](#example-2---per-world-playerlist)
|
||||
@@ -284,6 +284,22 @@ Here are a few tips how to do that:
|
||||
* Remove unnecessary brackets from prefix
|
||||
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
To access this feature, you'll need to obtain `LayoutManager` instance. Get it using `TabAPI.getInstance().getLayoutManager()`. If this feature is disabled, the method will return `null`.
|
||||
|
||||
The `LayoutManager` has the following methods:
|
||||
* `Layout createNewLayout(String name)` - Creates new layout with given internal name and 80 slots.
|
||||
* `Layout createNewLayout(String name, int slotCount)` - Creates new layout with given internal name and slot count (for 1.19.3+, <1.19.3 players will still see 80 slots).
|
||||
* `Layout getLayout(String name)` - Returns layout by given internal name. Works for layouts created in both configuration and through the API.
|
||||
* `sendLayout(TabPlayer player, Layout layout)` - Sends given layout to the player, overriding configuration. Sending `null` as layout will restore layout from configuration.
|
||||
|
||||
After creating a `Layout` using the API or getting it from configuration, you can use the following methods:
|
||||
* `String getName()` - Returns layout's name
|
||||
* `addFixedSlot(int slot, String text)` / `addFixedSlot(int slot, String text, String skin)` / `addFixedSlot(int slot, String text, int ping)` / `addFixedSlot(int slot, String text, String skin, int ping)` / `addFixedSlot(int slot, String text, String skin, String ping)` - Adds a fixed slot with gieven properties - slot, text, skin (optional) and ping (optional)
|
||||
* `addGroup(String condition, int[] slots)` - Adds a new player group with given condition (nullable for no condition) and slots. Condition can be either a conditional expression, or a condition created in config.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Per-server columns
|
||||
```
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# About the wiki
|
||||
The wiki explains the functionality of the plugin as of version **6.1.2**.
|
||||
The wiki explains the functionality of the plugin as of version **6.2.0**.
|
||||
If your config looks different or is missing some features, you are using an old version of the plugin.
|
||||
To get the latest version, check [releases](https://github.com/NEZNAMY/TAB/releases/).
|
||||
Wiki for older plugin versions is not available.
|
||||
|
||||
@@ -48,14 +48,12 @@ public interface LayoutManager {
|
||||
Layout getLayout(@NonNull String name);
|
||||
|
||||
/**
|
||||
* Sends layout to player. Set to {@code null} to make player not see
|
||||
* any layout, but default TabList instead.
|
||||
* Sends layout to player. Set to {@code null} to reset layout back to original one based on configuration.
|
||||
*
|
||||
* @param player
|
||||
* Player to send layout to
|
||||
* @param layout
|
||||
* Layout to send
|
||||
* @see #resetLayout(TabPlayer)
|
||||
*/
|
||||
void sendLayout(@NonNull TabPlayer player, @Nullable Layout layout);
|
||||
|
||||
@@ -65,6 +63,8 @@ public interface LayoutManager {
|
||||
* @param player
|
||||
* Player to reset layout for
|
||||
* @see #sendLayout(TabPlayer, Layout)
|
||||
* @deprecated Use {@link #sendLayout(TabPlayer, Layout)} with {@code null} instead
|
||||
*/
|
||||
@Deprecated
|
||||
void resetLayout(@NonNull TabPlayer player);
|
||||
}
|
||||
+1
-3
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "6.1.3"
|
||||
version = "6.2.1"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
@@ -27,8 +27,6 @@ val platformPaths = setOf(
|
||||
":bukkit:v1_16_R3",
|
||||
":bukkit:v1_17_R1",
|
||||
":bukkit:v1_18_R2",
|
||||
":bukkit:v1_19_R1",
|
||||
":bukkit:v1_19_R2",
|
||||
":bukkit:v1_19_R3",
|
||||
":bukkit:v1_20_R1",
|
||||
":bukkit:v1_20_R2",
|
||||
|
||||
+5
-3
@@ -14,14 +14,16 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for Paper 26.2.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for Paper 26.2 - 26.3.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
/** Flag tracking if this server is Canvas 26.1.2+ with new scoreboard checks */
|
||||
private final boolean isCanvas = ReflectionUtils.classExists("io.canvasmc.canvas.world.scores.TeamData") ||
|
||||
ReflectionUtils.classExists("io.canvasmc.canvas.threadedregions.scores.TeamData");
|
||||
private final boolean isCanvas =
|
||||
ReflectionUtils.classExists("io.canvasmc.canvas.world.scores.TeamData") ||
|
||||
ReflectionUtils.classExists("io.canvasmc.canvas.threadedregions.scores.TeamData") ||
|
||||
ReflectionUtils.classExists("io.canvasmc.canvas.threadedregions.world.scores.TeamData");
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
|
||||
|
||||
@@ -28,7 +28,7 @@ public class BukkitTAB extends JavaPlugin {
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Use the other jar in the release instead.");
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion() + ") is not supported.");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.12.2, 1.16.5, 1.17.1 and 1.18.2 - 26.2");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.12.2, 1.16.5, 1.17.1, 1.18.2 and 1.19.4 - 26.3");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you just updated to a new Minecraft version, check for TAB updates.");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you are using an unsupported 1.x version, use an older version of TAB (latest TAB 5.x supports all MC 1.x versions).");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Thrown error message: " + e.getMessage());
|
||||
|
||||
@@ -96,6 +96,7 @@ public class ServerVersionInfo {
|
||||
spigotVersions.put(ProtocolVersion.V26_1_1, "v26_1");
|
||||
spigotVersions.put(ProtocolVersion.V26_1_2, "v26_1");
|
||||
spigotVersions.put(ProtocolVersion.V26_2, "v26_2");
|
||||
spigotVersions.put(ProtocolVersion.V26_3, "v26_2");
|
||||
|
||||
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
|
||||
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
|
||||
@@ -116,6 +117,7 @@ public class ServerVersionInfo {
|
||||
paperVersions.put(ProtocolVersion.V26_1_1, "paper_1_21_11"); // v26_1 works too
|
||||
paperVersions.put(ProtocolVersion.V26_1_2, "paper_1_21_11"); // v26_1 works too
|
||||
paperVersions.put(ProtocolVersion.V26_2, "paper_26_2"); // v26_2 works too
|
||||
paperVersions.put(ProtocolVersion.V26_3, "paper_26_2");
|
||||
|
||||
if (serverVersion == ProtocolVersion.UNKNOWN) {
|
||||
throw new IllegalStateException(String.format(
|
||||
|
||||
@@ -238,9 +238,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
Metrics metrics = new Metrics(plugin, TabConstants.BSTATS_PLUGIN_ID_BUKKIT);
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM,
|
||||
() -> TAB.getInstance().getGroupManager().getPermissionPlugin()));
|
||||
metrics.addCustomChart(new SimplePie("tab_6_1_0_servers",
|
||||
metrics.addCustomChart(new SimplePie("tab_6_2_x_servers",
|
||||
() -> serverVersionInfo.getServerName() + " " + serverVersionInfo.getServerVersion().getFriendlyName()));
|
||||
metrics.addCustomChart(new SimplePie("tab_6_1_0_package", serverVersionInfo::getImplementationPackage));
|
||||
metrics.addCustomChart(new SimplePie("tab_6_2_x_package", serverVersionInfo::getImplementationPackage));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.spigotmc:spigot:1.19.2-R0.1-SNAPSHOT") {
|
||||
// Broken dependencies
|
||||
exclude("com.mojang", "authlib")
|
||||
exclude("com.mojang", "logging")
|
||||
exclude("com.mojang", "datafixerupper")
|
||||
}
|
||||
compileOnly("com.mojang:authlib:1.5.25")
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
|
||||
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
|
||||
import net.minecraft.network.chat.*;
|
||||
import net.minecraft.resources.MinecraftKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Component converter using direct NMS code.
|
||||
*/
|
||||
public class NMSComponentConverter extends ComponentConverter<IChatBaseComponent> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newTextComponent(@NotNull String text) {
|
||||
return IChatBaseComponent.b(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newTranslatableComponent(@NotNull String key) {
|
||||
return IChatBaseComponent.c(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newKeybindComponent(@NotNull String keybind) {
|
||||
return IChatBaseComponent.d(keybind);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newObjectComponent(@NotNull TabAtlasSprite sprite) {
|
||||
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newObjectComponent(@NotNull TabPlayerSprite sprite) {
|
||||
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull IChatBaseComponent nmsComponent, @NotNull TabStyle modifier) {
|
||||
((IChatMutableComponent)nmsComponent).c(ChatModifier.a
|
||||
.a(modifier.getColor() == null ? null : ChatHexColor.a(modifier.getColor().getRgb()))
|
||||
.a(modifier.getBold())
|
||||
.b(modifier.getItalic())
|
||||
.c(modifier.getUnderlined())
|
||||
.d(modifier.getStrikethrough())
|
||||
.e(modifier.getObfuscated())
|
||||
.a(modifier.getFont() == null ? null : MinecraftKey.a(modifier.getFont()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSibling(@NotNull IChatBaseComponent parent, @NotNull IChatBaseComponent child) {
|
||||
((IChatMutableComponent)parent).b(child);
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.19.1 - 1.19.2.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().b.b.m;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import net.minecraft.EnumChatFormat;
|
||||
import net.minecraft.network.chat.IChatBaseComponent;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
|
||||
import net.minecraft.server.ScoreboardServer;
|
||||
import net.minecraft.world.scores.Scoreboard;
|
||||
import net.minecraft.world.scores.ScoreboardObjective;
|
||||
import net.minecraft.world.scores.ScoreboardTeam;
|
||||
import net.minecraft.world.scores.ScoreboardTeamBase;
|
||||
import net.minecraft.world.scores.criteria.IScoreboardCriteria;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
|
||||
private static final Constructor<PacketPlayOutScoreboardTeam> teamConstructor;
|
||||
|
||||
static {
|
||||
try {
|
||||
teamConstructor = PacketPlayOutScoreboardTeam.class.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class);
|
||||
teamConstructor.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
*
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = new ScoreboardObjective(
|
||||
dummyScoreboard,
|
||||
objective.getName(),
|
||||
IScoreboardCriteria.a,
|
||||
objective.getTitle().convert(),
|
||||
IScoreboardCriteria.EnumScoreboardHealthDisplay.values()[objective.getHealthDisplay().ordinal()]
|
||||
);
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sendPacket(new PacketPlayOutScoreboardDisplayObjective(
|
||||
objective.getDisplaySlot().ordinal(),
|
||||
(ScoreboardObjective) objective.getPlatformObjective()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
sendPacket(new PacketPlayOutScoreboardObjective((ScoreboardObjective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
|
||||
obj.a((IChatBaseComponent) objective.getTitle().convert());
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(
|
||||
ScoreboardServer.Action.a,
|
||||
score.getObjective().getName(),
|
||||
score.getHolder(),
|
||||
score.getValue()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(
|
||||
ScoreboardServer.Action.b,
|
||||
score.getObjective().getName(),
|
||||
score.getHolder(),
|
||||
score.getValue()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object createTeam(@NonNull String name) {
|
||||
return new ScoreboardTeam(dummyScoreboard, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.g().addAll(team.getPlayers());
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a(t, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam(), false));
|
||||
}
|
||||
|
||||
private void updateTeamProperties(@NonNull Team team) {
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.a((team.getOptions() & 0x01) != 0);
|
||||
t.b((team.getOptions() & 0x02) != 0);
|
||||
t.a(visibilities[team.getVisibility().ordinal()]);
|
||||
t.a(collisions[team.getCollision().ordinal()]);
|
||||
t.b((IChatBaseComponent) team.getPrefix().convert());
|
||||
t.c(team.getSuffix().convert());
|
||||
t.a(EnumChatFormat.valueOf(team.getColor().name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
((PacketPlayOutScoreboardDisplayObjective)packet).b(),
|
||||
((PacketPlayOutScoreboardDisplayObjective)packet).c()
|
||||
);
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardObjective) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(
|
||||
player,
|
||||
((PacketPlayOutScoreboardObjective)packet).d(),
|
||||
((PacketPlayOutScoreboardObjective)packet).b()
|
||||
);
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players != null) {
|
||||
String name = ((PacketPlayOutScoreboardTeam) packet).d();
|
||||
return teamConstructor.newInstance(
|
||||
name,
|
||||
action,
|
||||
((PacketPlayOutScoreboardTeam) packet).f(),
|
||||
onTeamPacket(action, name, players)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
if (team.c() == PacketPlayOutScoreboardTeam.a.a) {
|
||||
return 0;
|
||||
} else if (team.c() == PacketPlayOutScoreboardTeam.a.b) {
|
||||
return 1;
|
||||
} else if (team.b() == PacketPlayOutScoreboardTeam.a.a) {
|
||||
return 3;
|
||||
} else if (team.b() == PacketPlayOutScoreboardTeam.a.b) {
|
||||
return 4;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
|
||||
}
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import net.minecraft.network.chat.IChatBaseComponent;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter;
|
||||
import net.minecraft.world.entity.player.ProfilePublicKey;
|
||||
import net.minecraft.world.level.EnumGamemode;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
|
||||
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
|
||||
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
|
||||
private static final EnumPlayerInfoAction UPDATE_DISPLAY_NAME = EnumPlayerInfoAction.d;
|
||||
private static final EnumPlayerInfoAction REMOVE_PLAYER = EnumPlayerInfoAction.e;
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeEntry(@NonNull UUID entry) {
|
||||
sendPacket(EnumPlayerInfoAction.e, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(EnumPlayerInfoAction.d, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(EnumPlayerInfoAction.c, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(EnumPlayerInfoAction.b, entry, "", null, 0, gameMode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListed(@NonNull UUID entry, boolean listed) {
|
||||
// Added in 1.19.3
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListOrder(@NonNull UUID entry, int listOrder) {
|
||||
// Added in 1.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(EnumPlayerInfoAction.a, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.getLatency(),
|
||||
entry.getGameMode(), entry.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
sendPacket(new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
|
||||
if (properties.isEmpty()) return null; // Offline mode
|
||||
Property property = properties.iterator().next();
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = info.c();
|
||||
List<PlayerInfoData> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (PlayerInfoData nmsData : info.b()) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = nmsData.d();
|
||||
int latency = nmsData.b();
|
||||
int gameMode = nmsData.c().a();
|
||||
ProfilePublicKey.a publicKey = nmsData.e();
|
||||
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new PlayerInfoData(
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName,
|
||||
publicKey
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
newPacket.b().clear();
|
||||
newPacket.b().addAll(updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
packet.b().add(new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
* @param id
|
||||
* Profile ID
|
||||
* @param name
|
||||
* Profile name
|
||||
* @param skin
|
||||
* Player skin
|
||||
* @return GameProfile from given parameters
|
||||
*/
|
||||
@NotNull
|
||||
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
|
||||
GameProfile profile = new GameProfile(id, name);
|
||||
if (skin != null) {
|
||||
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
|
||||
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.valueOf("ADD_PLAYER");
|
||||
private static final EnumPlayerInfoAction REMOVE_PLAYER = EnumPlayerInfoAction.valueOf("REMOVE_PLAYER");
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerInfo) {
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
for (PacketPlayOutPlayerInfo.PlayerInfoData nmsData : info.b()) {
|
||||
if (info.c() == ADD_PLAYER) {
|
||||
tablistEntries.add(nmsData.a().getId());
|
||||
}
|
||||
if (info.c() == REMOVE_PLAYER) {
|
||||
tablistEntries.remove(nmsData.a().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT") {
|
||||
// Broken dependencies
|
||||
exclude("com.mojang", "authlib")
|
||||
exclude("com.mojang", "logging")
|
||||
exclude("com.mojang", "datafixerupper")
|
||||
}
|
||||
compileOnly("com.mojang:authlib:1.5.25")
|
||||
}
|
||||
|
||||
tasks.compileJava {
|
||||
options.release.set(14) // Records
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
|
||||
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
|
||||
import net.minecraft.network.chat.*;
|
||||
import net.minecraft.resources.MinecraftKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Component converter using direct NMS code.
|
||||
*/
|
||||
public class NMSComponentConverter extends ComponentConverter<IChatBaseComponent> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newTextComponent(@NotNull String text) {
|
||||
return IChatBaseComponent.b(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newTranslatableComponent(@NotNull String key) {
|
||||
return IChatBaseComponent.c(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newKeybindComponent(@NotNull String keybind) {
|
||||
return IChatBaseComponent.d(keybind);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newObjectComponent(@NotNull TabAtlasSprite sprite) {
|
||||
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IChatBaseComponent newObjectComponent(@NotNull TabPlayerSprite sprite) {
|
||||
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull IChatBaseComponent nmsComponent, @NotNull TabStyle modifier) {
|
||||
((IChatMutableComponent)nmsComponent).c(ChatModifier.a
|
||||
.a(modifier.getColor() == null ? null : ChatHexColor.a(modifier.getColor().getRgb()))
|
||||
.a(modifier.getBold())
|
||||
.b(modifier.getItalic())
|
||||
.c(modifier.getUnderlined())
|
||||
.d(modifier.getStrikethrough())
|
||||
.e(modifier.getObfuscated())
|
||||
.a(modifier.getFont() == null ? null : MinecraftKey.a(modifier.getFont()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSibling(@NotNull IChatBaseComponent parent, @NotNull IChatBaseComponent child) {
|
||||
((IChatMutableComponent)parent).b(child);
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.19.3.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().b.b.m;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import net.minecraft.EnumChatFormat;
|
||||
import net.minecraft.network.chat.IChatBaseComponent;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
|
||||
import net.minecraft.server.ScoreboardServer;
|
||||
import net.minecraft.world.scores.Scoreboard;
|
||||
import net.minecraft.world.scores.ScoreboardObjective;
|
||||
import net.minecraft.world.scores.ScoreboardTeam;
|
||||
import net.minecraft.world.scores.ScoreboardTeamBase;
|
||||
import net.minecraft.world.scores.criteria.IScoreboardCriteria;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
|
||||
private static final Constructor<PacketPlayOutScoreboardTeam> teamConstructor;
|
||||
|
||||
static {
|
||||
try {
|
||||
teamConstructor = PacketPlayOutScoreboardTeam.class.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class);
|
||||
teamConstructor.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
*
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = new ScoreboardObjective(
|
||||
dummyScoreboard,
|
||||
objective.getName(),
|
||||
IScoreboardCriteria.a,
|
||||
objective.getTitle().convert(),
|
||||
IScoreboardCriteria.EnumScoreboardHealthDisplay.values()[objective.getHealthDisplay().ordinal()]
|
||||
);
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sendPacket(new PacketPlayOutScoreboardDisplayObjective(
|
||||
objective.getDisplaySlot().ordinal(),
|
||||
(ScoreboardObjective) objective.getPlatformObjective()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
sendPacket(new PacketPlayOutScoreboardObjective((ScoreboardObjective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
|
||||
obj.a((IChatBaseComponent) objective.getTitle().convert());
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(
|
||||
ScoreboardServer.Action.a,
|
||||
score.getObjective().getName(),
|
||||
score.getHolder(),
|
||||
score.getValue()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(
|
||||
ScoreboardServer.Action.b,
|
||||
score.getObjective().getName(),
|
||||
score.getHolder(),
|
||||
score.getValue()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object createTeam(@NonNull String name) {
|
||||
return new ScoreboardTeam(dummyScoreboard, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.g().addAll(team.getPlayers());
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a(t, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam(), false));
|
||||
}
|
||||
|
||||
private void updateTeamProperties(@NonNull Team team) {
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.a((team.getOptions() & 0x01) != 0);
|
||||
t.b((team.getOptions() & 0x02) != 0);
|
||||
t.a(visibilities[team.getVisibility().ordinal()]);
|
||||
t.a(collisions[team.getCollision().ordinal()]);
|
||||
t.b((IChatBaseComponent) team.getPrefix().convert());
|
||||
t.c(team.getSuffix().convert());
|
||||
t.a(EnumChatFormat.valueOf(team.getColor().name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
((PacketPlayOutScoreboardDisplayObjective)packet).b(),
|
||||
((PacketPlayOutScoreboardDisplayObjective)packet).c()
|
||||
);
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardObjective) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(
|
||||
player,
|
||||
((PacketPlayOutScoreboardObjective)packet).d(),
|
||||
((PacketPlayOutScoreboardObjective)packet).b()
|
||||
);
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players != null) {
|
||||
String name = ((PacketPlayOutScoreboardTeam) packet).d();
|
||||
return teamConstructor.newInstance(
|
||||
name,
|
||||
action,
|
||||
((PacketPlayOutScoreboardTeam) packet).f(),
|
||||
onTeamPacket(action, name, players)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
if (team.c() == PacketPlayOutScoreboardTeam.a.a) {
|
||||
return 0;
|
||||
} else if (team.c() == PacketPlayOutScoreboardTeam.a.b) {
|
||||
return 1;
|
||||
} else if (team.b() == PacketPlayOutScoreboardTeam.a.a) {
|
||||
return 3;
|
||||
} else if (team.b() == PacketPlayOutScoreboardTeam.a.b) {
|
||||
return 4;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
|
||||
}
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.IChatBaseComponent;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter;
|
||||
import net.minecraft.world.level.EnumGamemode;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
private static final ClientboundPlayerInfoUpdatePacket.a ADD_PLAYER = ClientboundPlayerInfoUpdatePacket.a.valueOf("ADD_PLAYER");
|
||||
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_GAME_MODE = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_GAME_MODE");
|
||||
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_LATENCY = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_LATENCY");
|
||||
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_DISPLAY_NAME = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_DISPLAY_NAME");
|
||||
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_LISTED = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_LISTED");
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> ADD_PLAYER_SET = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.a.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_GAME_MODE_SET = EnumSet.of(UPDATE_GAME_MODE);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_DISPLAY_NAME_SET = EnumSet.of(UPDATE_DISPLAY_NAME);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeEntry(@NonNull UUID entry) {
|
||||
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(UPDATE_DISPLAY_NAME_SET, entry, "", null, false, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(UPDATE_LATENCY_SET, entry, "", null, false, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(UPDATE_GAME_MODE_SET, entry, "", null, false, 0, gameMode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListed(@NonNull UUID entry, boolean listed) {
|
||||
sendPacket(UPDATE_LISTED_SET, entry, "", null, listed, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListOrder(@NonNull UUID entry, int listOrder) {
|
||||
// Added in 1.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(ADD_PLAYER_SET, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
|
||||
entry.getGameMode(), entry.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
sendPacket(new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
|
||||
if (properties.isEmpty()) return null; // Offline mode
|
||||
Property property = properties.iterator().next();
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return packet;
|
||||
ClientboundPlayerInfoUpdatePacket info = (ClientboundPlayerInfoUpdatePacket) packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.b();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (ClientboundPlayerInfoUpdatePacket.b nmsData : info.c()) {
|
||||
UUID profileId = nmsData.a();
|
||||
boolean rewriteEntry = false;
|
||||
IChatBaseComponent displayName = nmsData.f();
|
||||
int latency = nmsData.d();
|
||||
int gameMode = nmsData.e().a();
|
||||
boolean listed = nmsData.c();
|
||||
if (actions.contains(UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(profileId);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_LATENCY)) {
|
||||
if (getForcedLatency() != null) {
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && !LayoutManagerImpl.UUIDS_SET.contains(nmsData.a())) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, profileId, nmsData.b().getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.b(
|
||||
profileId, nmsData.b(), listed, latency, EnumGamemode.a(gameMode), displayName,
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.a> action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, Collections.emptyList());
|
||||
PLAYERS.set(packet, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.b(
|
||||
id,
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
* @param id
|
||||
* Profile ID
|
||||
* @param name
|
||||
* Profile name
|
||||
* @param skin
|
||||
* Player skin
|
||||
* @return GameProfile from given parameters
|
||||
*/
|
||||
@NotNull
|
||||
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
|
||||
GameProfile profile = new GameProfile(id, name);
|
||||
if (skin != null) {
|
||||
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
|
||||
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.a;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
private static final a ADD_PLAYER = a.valueOf("ADD_PLAYER");
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket) {
|
||||
ClientboundPlayerInfoRemovePacket remove = (ClientboundPlayerInfoRemovePacket) packet;
|
||||
for (UUID id : remove.b()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket update = (ClientboundPlayerInfoUpdatePacket) packet;
|
||||
if (update.b().contains(ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.b nmsData : update.c()) {
|
||||
tablistEntries.add(nmsData.a());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,12 @@ repositories {
|
||||
maven("https://repo.papermc.io/repository/maven-public/") // Adventure
|
||||
}
|
||||
|
||||
val minecraftVersion = "26.2"
|
||||
val minecraftVersion = "26.3"
|
||||
|
||||
// Fabric API versions for each Minecraft version for easier backporting
|
||||
// Official website (for updating in the future): https://fabricmc.net/develop/
|
||||
val fabricApiVersions = mapOf(
|
||||
"26.3" to "0.160.5+26.3",
|
||||
"26.2" to "0.150.3+26.2",
|
||||
"26.1.2" to "0.150.0+26.1.2"
|
||||
)
|
||||
|
||||
@@ -133,7 +133,12 @@ public class FabricScoreboard extends SafeScoreboard<FabricTabPlayer> {
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int method = getMethod(team);
|
||||
if (method != TeamAction.UPDATE) {
|
||||
team.players = onTeamPacket(method, team.getName(), team.getPlayers());
|
||||
return new ClientboundSetPlayerTeamPacket(
|
||||
team.getName(),
|
||||
method,
|
||||
team.getParameters(),
|
||||
onTeamPacket(method, team.getName(), team.getPlayers())
|
||||
);
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"fabric-api-base": "*",
|
||||
"java": ">=25",
|
||||
"minecraft": [
|
||||
"26.2"
|
||||
"26.2",
|
||||
"26.3"
|
||||
]
|
||||
},
|
||||
"recommends": {
|
||||
|
||||
@@ -5,8 +5,7 @@ accessible field net/minecraft/server/network/ServerCommonPacketListenerImpl con
|
||||
accessible field net/minecraft/network/Connection channel Lio/netty/channel/Channel;
|
||||
|
||||
# Scoreboard team
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
|
||||
mutable field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
|
||||
accessible method net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket <init> (Ljava/lang/String;ILjava/util/Optional;Ljava/util/Collection;)V
|
||||
|
||||
# Player info
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket entries Ljava/util/List;
|
||||
|
||||
@@ -15,13 +15,14 @@ repositories {
|
||||
maven("https://repo.papermc.io/repository/maven-public/") // Adventure
|
||||
}
|
||||
|
||||
val minecraftVersion = "26.2"
|
||||
val minecraftVersion = "26.3"
|
||||
|
||||
// Forge API versions for each Minecraft version for easier backporting
|
||||
// Official website (for updating in the future): https://files.minecraftforge.net/net/minecraftforge/forge/
|
||||
val forgeApiVersions = mapOf(
|
||||
"26.2" to "26.2-65.0.0",
|
||||
"26.1.2" to "26.1.2-64.0.4"
|
||||
"26.3" to "26.3-66.0.2",
|
||||
"26.2" to "26.2-65.1.3",
|
||||
"26.1.2" to "26.1.2-64.1.3"
|
||||
)
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -4,7 +4,6 @@ import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.numbers.FixedFormat;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
@@ -15,9 +14,7 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria;
|
||||
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -29,7 +26,6 @@ public class ForgeScoreboard extends SafeScoreboard<ForgeTabPlayer> {
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
@@ -139,7 +135,12 @@ public class ForgeScoreboard extends SafeScoreboard<ForgeTabPlayer> {
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int method = getMethod(team);
|
||||
if (method != TeamAction.UPDATE) {
|
||||
players.set(team, onTeamPacket(method, team.getName(), team.getPlayers()));
|
||||
return new ClientboundSetPlayerTeamPacket(
|
||||
team.getName(),
|
||||
method,
|
||||
team.getParameters(),
|
||||
onTeamPacket(method, team.getName(), team.getPlayers())
|
||||
);
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
|
||||
@@ -5,12 +5,10 @@ import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
@@ -20,7 +18,6 @@ import net.minecraft.world.level.GameType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -36,8 +33,6 @@ public class ForgeTabList extends TrackedTabList<ForgeTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -104,7 +99,6 @@ public class ForgeTabList extends TrackedTabList<ForgeTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
@@ -158,18 +152,17 @@ public class ForgeTabList extends TrackedTabList<ForgeTabPlayer> {
|
||||
}
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
entries.set(newPacket, updatedList);
|
||||
newPacket.entries = updatedList;
|
||||
return newPacket;
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder, boolean showHat) {
|
||||
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, Collections.emptyList());
|
||||
entries.set(packet, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
packet.entries = Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
id,
|
||||
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
|
||||
listed,
|
||||
@@ -179,7 +172,7 @@ public class ForgeTabList extends TrackedTabList<ForgeTabPlayer> {
|
||||
showHat,
|
||||
listOrder,
|
||||
null
|
||||
)));
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Scoreboard team
|
||||
public net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket <init>(Ljava/lang/String;ILjava/util/Optional;Ljava/util/Collection;)V
|
||||
|
||||
# Player info
|
||||
public-f net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket entries
|
||||
@@ -15,6 +15,6 @@ displayURL = "@website@"
|
||||
[[dependencies.tab]]
|
||||
modId = "minecraft"
|
||||
mandatory = true
|
||||
versionRange = "[26.2]"
|
||||
versionRange = "[26.2],[26.3]"
|
||||
ordering = "NONE"
|
||||
side = "SERVER"
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
|
||||
@@ -27,8 +27,6 @@ val platformPaths = setOf(
|
||||
":bukkit:v1_16_R3",
|
||||
":bukkit:v1_17_R1",
|
||||
":bukkit:v1_18_R2",
|
||||
":bukkit:v1_19_R1",
|
||||
":bukkit:v1_19_R2",
|
||||
":bukkit:v1_19_R3",
|
||||
":bukkit:v1_20_R1",
|
||||
":bukkit:v1_20_R2",
|
||||
|
||||
@@ -15,13 +15,14 @@ repositories {
|
||||
maven("https://repo.papermc.io/repository/maven-public/") // Adventure
|
||||
}
|
||||
|
||||
val minecraftVersion = "26.2"
|
||||
val minecraftVersion = "26.3"
|
||||
|
||||
// NeoForge API versions for each Minecraft version for easier backporting
|
||||
// Official website (for updating in the future): https://projects.neoforged.net/neoforged/neoforge
|
||||
val neoforgeApiVersions = mapOf(
|
||||
"26.2" to "26.2.0.1-beta",
|
||||
"26.1.2" to "26.1.2.17-beta"
|
||||
"26.3" to "26.3.0.1-beta",
|
||||
"26.2" to "26.2.0.88",
|
||||
"26.1.2" to "26.1.2.109"
|
||||
)
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -133,7 +133,12 @@ public class NeoForgeScoreboard extends SafeScoreboard<NeoForgeTabPlayer> {
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int method = getMethod(team);
|
||||
if (method != TeamAction.UPDATE) {
|
||||
team.players = onTeamPacket(method, team.getName(), team.getPlayers());
|
||||
return new ClientboundSetPlayerTeamPacket(
|
||||
team.getName(),
|
||||
method,
|
||||
team.getParameters(),
|
||||
onTeamPacket(method, team.getName(), team.getPlayers())
|
||||
);
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Scoreboard team
|
||||
public-f net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket players
|
||||
public net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket <init>(Ljava/lang/String;ILjava/util/Optional;Ljava/util/Collection;)V
|
||||
|
||||
# Player info
|
||||
public-f net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket entries
|
||||
@@ -15,6 +15,6 @@ description = '''@description@'''
|
||||
[[dependencies.tab]]
|
||||
modId = "minecraft"
|
||||
type = "required"
|
||||
versionRange = "[26.2]"
|
||||
versionRange = "[26.2],[26.3]"
|
||||
ordering = "NONE"
|
||||
side = "SERVER"
|
||||
@@ -52,8 +52,6 @@ include(":bukkit:v1_12_R1")
|
||||
include(":bukkit:v1_16_R3")
|
||||
include(":bukkit:v1_17_R1")
|
||||
include(":bukkit:v1_18_R2")
|
||||
include(":bukkit:v1_19_R1")
|
||||
include(":bukkit:v1_19_R2")
|
||||
include(":bukkit:v1_19_R3")
|
||||
include(":bukkit:v1_20_R1")
|
||||
include(":bukkit:v1_20_R2")
|
||||
|
||||
@@ -596,8 +596,12 @@ public class FeatureManager {
|
||||
if (config.getScoreboard() != null) {
|
||||
registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl(config.getScoreboard()));
|
||||
}
|
||||
registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber(config.getPlayerlistObjective()));
|
||||
registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName(config.getBelowname()));
|
||||
if (config.getPlayerlistObjective() != null) {
|
||||
registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber(config.getPlayerlistObjective()));
|
||||
}
|
||||
if (config.getBelowname() != null) {
|
||||
registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName(config.getBelowname()));
|
||||
}
|
||||
if (config.getSorting() != null) {
|
||||
registerFeature(TabConstants.Feature.SORTING, new Sorting(config.getSorting()));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public enum ProtocolVersion {
|
||||
|
||||
UNKNOWN,
|
||||
V26_3 (777),
|
||||
V26_2 (776),
|
||||
V26_1_2 (775),
|
||||
V26_1_1 (775),
|
||||
|
||||
@@ -149,9 +149,13 @@ public class DumpCommand extends SubCommand {
|
||||
mysql.put("password", "*CENSORED*");
|
||||
Map<String, Object> proxySupport = (Map<String, Object>) config.get("proxy-support");
|
||||
Map<String, Object> redis = (Map<String, Object>) proxySupport.get("redis");
|
||||
redis.put("url", "*CENSORED*");
|
||||
if (redis != null) {
|
||||
redis.put("url", "*CENSORED*");
|
||||
}
|
||||
Map<String, Object> rabbitmq = (Map<String, Object>) proxySupport.get("rabbitmq");
|
||||
rabbitmq.put("url", "*CENSORED*");
|
||||
if (rabbitmq != null) {
|
||||
rabbitmq.put("url", "*CENSORED*");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+82
-33
@@ -25,8 +25,9 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
|
||||
private final String PER_WORLD = "per-world";
|
||||
|
||||
private final String category;
|
||||
private final Collection<String> worldGroups = new ArrayList<>(this.<String, Object>getMap(PER_WORLD).keySet());
|
||||
private final Collection<String> serverGroups = new ArrayList<>(this.<String, Object>getMap(PER_SERVER).keySet());
|
||||
private final ConfigurationSection root = getConfigurationSection("");
|
||||
private final Collection<String> worldGroups = section(PER_WORLD).getKeys().stream().map(Object::toString).collect(Collectors.toList());
|
||||
private final Collection<String> serverGroups = section(PER_SERVER).getKeys().stream().map(Object::toString).collect(Collectors.toList());
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
@@ -48,26 +49,29 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
|
||||
public YamlPropertyConfigurationFile(@Nullable InputStream source, @NonNull File destination) throws IOException {
|
||||
super(source, destination);
|
||||
category = destination.getName().contains("groups") ? "group" : "user";
|
||||
for (Map.Entry<Object, Object> entry : getValues().entrySet()) {
|
||||
if (entry.getKey().equals(PER_SERVER)) {
|
||||
for (Object key : root.getKeys()) {
|
||||
String entry = key.toString();
|
||||
if (entry.equals(PER_SERVER)) {
|
||||
for (String server : serverGroups) {
|
||||
for (String name : this.<String, Object>getMap(PER_SERVER + "." + server).keySet()) {
|
||||
for (String property : this.<String, Object>getMap(PER_SERVER + "." + server + "." + name).keySet()) {
|
||||
checkProperty(destination.getName(), category, name, property, server, null, true);
|
||||
ConfigurationSection serverSection = section(PER_SERVER, server);
|
||||
for (Object name : serverSection.getKeys()) {
|
||||
for (Object property : serverSection.getConfigurationSection(name.toString()).getKeys()) {
|
||||
checkProperty(destination.getName(), category, name.toString(), property.toString(), server, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (entry.getKey().equals(PER_WORLD)) {
|
||||
} else if (entry.equals(PER_WORLD)) {
|
||||
for (String world : worldGroups) {
|
||||
for (String name : this.<String, Object>getMap(PER_WORLD + "." + world).keySet()) {
|
||||
for (String property : this.<String, Object>getMap(PER_WORLD + "." + world + "." + name).keySet()) {
|
||||
checkProperty(destination.getName(), category, name, property, null, world, true);
|
||||
ConfigurationSection worldSection = section(PER_WORLD, world);
|
||||
for (Object name : worldSection.getKeys()) {
|
||||
for (Object property : worldSection.getConfigurationSection(name.toString()).getKeys()) {
|
||||
checkProperty(destination.getName(), category, name.toString(), property.toString(), null, world, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (String property : this.<String, Object>getMap(entry.getKey().toString()).keySet()) {
|
||||
checkProperty(destination.getName(), category, entry.getKey().toString(), property, null, null, true);
|
||||
for (Object property : root.getConfigurationSection(entry).getKeys()) {
|
||||
checkProperty(destination.getName(), category, entry, property.toString(), null, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,13 +79,16 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
|
||||
|
||||
@Override
|
||||
public void setProperty(@NonNull String name, @NonNull String property, @Nullable Server server, @Nullable World world, @Nullable String value) {
|
||||
ConfigurationSection target;
|
||||
if (world != null) {
|
||||
set(String.format("%s.%s.%s.%s", PER_WORLD, world.getName(), name, property), fromString(value));
|
||||
target = getOrCreateSection(PER_WORLD, world.getName(), name);
|
||||
} else if (server != null) {
|
||||
set(String.format("%s.%s.%s.%s", PER_SERVER, server.getName(), name, property), fromString(value));
|
||||
target = getOrCreateSection(PER_SERVER, server.getName(), name);
|
||||
} else {
|
||||
set(String.format("%s.%s", name, property), fromString(value));
|
||||
target = getOrCreateSection(name);
|
||||
}
|
||||
target.put(property, fromString(value));
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,22 +96,22 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
|
||||
String worldName = world == null ? null : world.getName();
|
||||
String serverName = server == null ? null : server.getName();
|
||||
Object value;
|
||||
if ((value = getObject(new String[] {PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, worldName), name, property})) != null) {
|
||||
if ((value = section(PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, worldName), name).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + name + ", world=" + worldName};
|
||||
}
|
||||
if ((value = getObject(new String[] {PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, worldName), TabConstants.DEFAULT_GROUP, property})) != null) {
|
||||
if ((value = section(PER_WORLD, TAB.getInstance().getConfiguration().getGroup(worldGroups, worldName), TabConstants.DEFAULT_GROUP).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP + ", world=" + worldName};
|
||||
}
|
||||
if ((value = getObject(new String[] {PER_SERVER, TAB.getInstance().getConfiguration().getServerGroup(serverGroups, server), name, property})) != null) {
|
||||
if ((value = section(PER_SERVER, TAB.getInstance().getConfiguration().getServerGroup(serverGroups, server), name).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + name + ", server=" + serverName};
|
||||
}
|
||||
if ((value = getObject(new String[] {PER_SERVER, TAB.getInstance().getConfiguration().getServerGroup(serverGroups, server), TabConstants.DEFAULT_GROUP, property})) != null) {
|
||||
if ((value = section(PER_SERVER, TAB.getInstance().getConfiguration().getServerGroup(serverGroups, server), TabConstants.DEFAULT_GROUP).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP + ", server=" + serverName};
|
||||
}
|
||||
if ((value = getObject(new String[] {name, property})) != null) {
|
||||
if ((value = section(name).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + name};
|
||||
}
|
||||
if ((value = getObject(new String[] {TabConstants.DEFAULT_GROUP, property})) != null) {
|
||||
if ((value = section(TabConstants.DEFAULT_GROUP).getObject(property)) != null) {
|
||||
return new String[] {toString(value), category + "=" + TabConstants.DEFAULT_GROUP};
|
||||
}
|
||||
return new String[0];
|
||||
@@ -112,39 +119,81 @@ public class YamlPropertyConfigurationFile extends YamlConfigurationFile impleme
|
||||
|
||||
@Override
|
||||
public void remove(@NonNull String name) {
|
||||
set(name, null);
|
||||
getMap(PER_WORLD).keySet().forEach(world -> set(PER_WORLD + "." + world + "." + name, null));
|
||||
getMap(PER_SERVER).keySet().forEach(server -> set(PER_SERVER + "." + server + "." + name, null));
|
||||
root.remove(name);
|
||||
for (Object world : section(PER_WORLD).getKeys()) {
|
||||
section(PER_WORLD, world.toString()).remove(name);
|
||||
}
|
||||
for (Object server : section(PER_SERVER).getKeys()) {
|
||||
section(PER_SERVER, server.toString()).remove(name);
|
||||
}
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<String, Object> getGlobalSettings(@NonNull String name) {
|
||||
return getMap(name);
|
||||
Map<String, Object> settings = root.getMap(name);
|
||||
return settings == null ? new LinkedHashMap<>() : settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<String, Map<String, Object>> getPerWorldSettings(@NonNull String name) {
|
||||
return convertMap(getMap(PER_WORLD), name);
|
||||
return getSettingsByGroup(PER_WORLD, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<String, Map<String, Object>> getPerServerSettings(@NonNull String name) {
|
||||
return convertMap(getMap(PER_SERVER), name);
|
||||
return getSettingsByGroup(PER_SERVER, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<String> getAllEntries() {
|
||||
Set<Object> set = new HashSet<>(values.keySet());
|
||||
Set<Object> set = new HashSet<>(root.getKeys());
|
||||
set.remove(PER_WORLD);
|
||||
set.remove(PER_SERVER);
|
||||
Map<String, Map<String, Map<String, String>>> perWorld = getMap(PER_WORLD);
|
||||
perWorld.values().forEach(m -> set.addAll(m.keySet()));
|
||||
Map<String, Map<String, Map<String, String>>> perServer = getMap(PER_SERVER);
|
||||
perServer.values().forEach(m -> set.addAll(m.keySet()));
|
||||
addNestedEntryNames(set, PER_WORLD);
|
||||
addNestedEntryNames(set, PER_SERVER);
|
||||
return set.stream().map(Object::toString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ConfigurationSection section(@NotNull String... path) {
|
||||
ConfigurationSection section = root;
|
||||
for (String key : path) {
|
||||
section = section.getConfigurationSection(key);
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ConfigurationSection getOrCreateSection(@NotNull String... path) {
|
||||
ConfigurationSection section = root;
|
||||
for (String key : path) {
|
||||
if (section.getMap(key) == null) {
|
||||
section.put(key, new LinkedHashMap<>());
|
||||
}
|
||||
section = section.getConfigurationSection(key);
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Map<String, Object>> getSettingsByGroup(@NotNull String sectionName, @NotNull String name) {
|
||||
Map<String, Map<String, Object>> settings = new HashMap<>();
|
||||
ConfigurationSection sections = section(sectionName);
|
||||
for (Object key : sections.getKeys()) {
|
||||
settings.put(key.toString(), sections.getConfigurationSection(key.toString()).getMap(name));
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private void addNestedEntryNames(@NotNull Set<Object> entries, @NotNull String sectionName) {
|
||||
ConfigurationSection sections = section(sectionName);
|
||||
for (Object key : sections.getKeys()) {
|
||||
entries.addAll(sections.getConfigurationSection(key.toString()).getKeys());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class Config {
|
||||
@NotNull private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("config/config.yml"),
|
||||
new File(TAB.getInstance().getDataFolder(), "config.yml"));
|
||||
|
||||
@NotNull private final BelowNameConfiguration belowname;
|
||||
@Nullable private BelowNameConfiguration belowname;
|
||||
@Nullable private BossBarConfiguration bossbar;
|
||||
@NotNull private final ConditionsSection conditions;
|
||||
@Nullable private GlobalPlayerListConfiguration globalPlayerList;
|
||||
@@ -52,7 +52,7 @@ public class Config {
|
||||
@NotNull private final PlaceholderRefreshConfiguration refresh;
|
||||
@NotNull private final PlaceholderReplacementsConfiguration replacements;
|
||||
@NotNull private final PlaceholdersConfiguration placeholders;
|
||||
@NotNull private final PlayerListObjectiveConfiguration playerlistObjective;
|
||||
@Nullable private PlayerListObjectiveConfiguration playerlistObjective;
|
||||
@Nullable private ScoreboardConfiguration scoreboard;
|
||||
@Nullable private SortingConfiguration sorting;
|
||||
@Nullable private TablistFormattingConfiguration tablistFormatting;
|
||||
@@ -95,10 +95,10 @@ public class Config {
|
||||
replacements = PlaceholderReplacementsConfiguration.fromSection(config.getConfigurationSection("placeholder-output-replacements"));
|
||||
placeholders = PlaceholdersConfiguration.fromSection(config.getConfigurationSection("placeholders"));
|
||||
components = ComponentConfiguration.fromSection(config.getConfigurationSection("components"));
|
||||
belowname = BelowNameConfiguration.fromSection(config.getConfigurationSection("belowname-objective"));
|
||||
playerlistObjective = PlayerListObjectiveConfiguration.fromSection(config.getConfigurationSection("playerlist-objective"));
|
||||
|
||||
if (config.getBoolean("bossbar.enabled", false)) bossbar = BossBarConfiguration.fromSection(config.getConfigurationSection("bossbar"));
|
||||
if (config.getBoolean("belowname-objective.enabled", false)) belowname = BelowNameConfiguration.fromSection(config.getConfigurationSection("belowname-objective"));
|
||||
if (config.getBoolean("playerlist-objective.enabled", true)) playerlistObjective = PlayerListObjectiveConfiguration.fromSection(config.getConfigurationSection("playerlist-objective"));
|
||||
if (config.getBoolean("global-playerlist.enabled", false)) globalPlayerList = GlobalPlayerListConfiguration.fromSection(config.getConfigurationSection("global-playerlist"));
|
||||
if (config.getBoolean("header-footer.enabled", true)) headerFooter = HeaderFooterConfiguration.fromSection(config.getConfigurationSection("header-footer"));
|
||||
if (config.getBoolean("layout.enabled", false)) layout = LayoutConfiguration.fromSection(config.getConfigurationSection("layout"));
|
||||
@@ -118,7 +118,7 @@ public class Config {
|
||||
" 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.");
|
||||
}
|
||||
if (playerlistObjective.isEnabled()) {
|
||||
if (playerlistObjective != null) {
|
||||
TAB.getInstance().getConfigHelper().startup().startupWarn(config.getFile(), "Layout feature breaks playerlist-objective feature, because it replaces real player with fake slots " +
|
||||
"with different usernames for more reliable functionality. Disable playerlist-objective feature, as it will only look bad " +
|
||||
"and consume resources.");
|
||||
|
||||
@@ -495,6 +495,8 @@ public class PlaceholderManagerImpl extends RefreshableFeature implements Placeh
|
||||
public void registerServerPlaceholder(@NonNull Pattern identifier, int refresh, @NonNull Function<Matcher, Supplier<String>> function) {
|
||||
ensureActive();
|
||||
registeredDynamicPlaceholders.put(identifier, id -> groups -> new ServerPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
replaceMatchingPlaceholders(identifier,
|
||||
(id, groups) -> new ServerPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -502,6 +504,8 @@ public class PlaceholderManagerImpl extends RefreshableFeature implements Placeh
|
||||
@NonNull Function<Matcher, Function<me.neznamy.tab.api.TabPlayer, String>> function) {
|
||||
ensureActive();
|
||||
registeredDynamicPlaceholders.put(identifier, id -> groups -> new PlayerPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
replaceMatchingPlaceholders(identifier,
|
||||
(id, groups) -> new PlayerPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -509,6 +513,18 @@ public class PlaceholderManagerImpl extends RefreshableFeature implements Placeh
|
||||
@NonNull Function<Matcher, BiFunction<me.neznamy.tab.api.TabPlayer, me.neznamy.tab.api.TabPlayer, String>> function) {
|
||||
ensureActive();
|
||||
registeredDynamicPlaceholders.put(identifier, id -> groups -> new RelationalPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
replaceMatchingPlaceholders(identifier,
|
||||
(id, groups) -> new RelationalPlaceholderImpl(id, refresh, function.apply(groups)));
|
||||
}
|
||||
|
||||
private void replaceMatchingPlaceholders(@NotNull Pattern identifier,
|
||||
@NotNull BiFunction<String, Matcher, ? extends Placeholder> factory) {
|
||||
for (Entry<String, PlaceholderReference> entry : new ArrayList<>(registeredPlaceholders.entrySet())) {
|
||||
Matcher matcher = identifier.matcher(entry.getKey());
|
||||
if (!matcher.matches()) continue;
|
||||
bridgePlaceholders.remove(entry.getKey());
|
||||
registerPlaceholder(factory.apply(entry.getKey(), matcher));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -150,15 +150,11 @@ public class BelowName extends RefreshableFeature implements JoinListener, QuitL
|
||||
p.belowNameData.disabled.set(!forced);
|
||||
return;
|
||||
}
|
||||
if (!configuration.isEnabled()) {
|
||||
p.belowNameData.disabled.set(true);
|
||||
return;
|
||||
}
|
||||
updateVisibility(p, disabledNow);
|
||||
}
|
||||
|
||||
private boolean configuredDisabled(@NotNull TabPlayer p) {
|
||||
return !configuration.isEnabled() || disableChecker.isDisableConditionMet(p);
|
||||
return disableChecker.isDisableConditionMet(p);
|
||||
}
|
||||
|
||||
private void applyDisabledState(@NotNull TabPlayer p, boolean disabled) {
|
||||
@@ -294,9 +290,10 @@ public class BelowName extends RefreshableFeature implements JoinListener, QuitL
|
||||
}
|
||||
|
||||
private void updatePlayer(@NotNull TabPlayer player) {
|
||||
int value = getValue(player);
|
||||
for (TabPlayer all : onlinePlayers.getPlayers()) {
|
||||
setScore(player, all, getValue(all), all.belowNameData.fancyValue.getFormat(player));
|
||||
if (all != player) setScore(all, player, getValue(player), player.belowNameData.fancyValue.getFormat(all));
|
||||
if (all != player) setScore(player, all, getValue(all), all.belowNameData.fancyValue.getFormat(player));
|
||||
setScore(all, player, value, player.belowNameData.fancyValue.getFormat(all));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,8 +327,9 @@ public class BelowName extends RefreshableFeature implements JoinListener, QuitL
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull TabPlayer player) {
|
||||
if (player.isVanished()) return;
|
||||
int value = getValue(player);
|
||||
for (TabPlayer viewer : onlinePlayers.getPlayers()) {
|
||||
setScore(viewer, player, getValue(player), player.belowNameData.fancyValue.getFormat(viewer));
|
||||
setScore(viewer, player, value, player.belowNameData.fancyValue.getFormat(viewer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -17,7 +17,6 @@ import java.util.Arrays;
|
||||
public class BelowNameConfiguration {
|
||||
|
||||
@NotNull private final ConfigurationSection section;
|
||||
private final boolean enabled;
|
||||
@NotNull private final String value;
|
||||
@NotNull private final String title;
|
||||
@NotNull private final String fancyValue;
|
||||
@@ -71,7 +70,6 @@ public class BelowNameConfiguration {
|
||||
|
||||
return new BelowNameConfiguration(
|
||||
section,
|
||||
section.getBoolean("enabled", false),
|
||||
value,
|
||||
title,
|
||||
section.getString("fancy-value", "&c" + TabConstants.Placeholder.HEALTH),
|
||||
|
||||
@@ -77,18 +77,10 @@ public class LayoutManagerImpl extends RefreshableFeature implements LayoutManag
|
||||
p.layoutData.sortingString = p.sortingData.fullTeamName;
|
||||
sortedPlayers.put(p, p.sortingData.fullTeamName);
|
||||
LayoutPattern highest = getHighestLayout(p);
|
||||
if (highest != null) {
|
||||
sendLayout(p, highest);
|
||||
}
|
||||
sendLayout(p, highest);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all.layoutData.currentLayout != null) all.layoutData.currentLayout.view.onJoin(p);
|
||||
}
|
||||
|
||||
// Unformat original entries for players who can see a layout to avoid spaces due to unparsed placeholders and such
|
||||
if (highest == null) return;
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
p.getTabList().updateDisplayName(all, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,17 +104,30 @@ public class LayoutManagerImpl extends RefreshableFeature implements LayoutManag
|
||||
LayoutPattern current = p.layoutData.currentLayout == null ? null : p.layoutData.currentLayout.view.getPattern();
|
||||
if (highest != current) {
|
||||
if (current != null) p.layoutData.currentLayout.view.destroy();
|
||||
p.layoutData.currentLayout = null;
|
||||
if (highest != null) {
|
||||
sendLayout(p, highest);
|
||||
}
|
||||
sendLayout(p, highest);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLayout(@NotNull TabPlayer player, @NotNull LayoutPattern pattern) {
|
||||
LayoutBase view = new FakeEntryLayout(this, pattern, player);
|
||||
player.layoutData.currentLayout = new LayoutData(view);
|
||||
view.send();
|
||||
private void sendLayout(@NotNull TabPlayer player, @Nullable LayoutPattern pattern) {
|
||||
if (pattern != null) {
|
||||
LayoutBase view = new FakeEntryLayout(this, pattern, player);
|
||||
player.layoutData.currentLayout = new LayoutData(view);
|
||||
view.send();
|
||||
if (playerList != null) {
|
||||
// Unformat original entries for players who can see a layout to avoid spaces due to unparsed placeholders and such
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
player.getTabList().updateDisplayName(all, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
player.layoutData.currentLayout = null;
|
||||
if (playerList != null) {
|
||||
// Reformat original entries for players without any layout
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
player.getTabList().updateDisplayName(all, playerList.getTabFormat(all, player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,11 +215,7 @@ public class LayoutManagerImpl extends RefreshableFeature implements LayoutManag
|
||||
|
||||
@Override
|
||||
public void resetLayout(@NonNull me.neznamy.tab.api.TabPlayer player) {
|
||||
ensureActive();
|
||||
TabPlayer p = (TabPlayer) player;
|
||||
p.ensureLoaded();
|
||||
p.layoutData.forcedLayout = null;
|
||||
refresh(p, false);
|
||||
sendLayout(player, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
-2
@@ -18,7 +18,6 @@ import java.util.Arrays;
|
||||
public class PlayerListObjectiveConfiguration {
|
||||
|
||||
@NotNull private final ConfigurationSection section;
|
||||
private final boolean enabled;
|
||||
@NotNull private final String value;
|
||||
@NotNull private final String fancyValue;
|
||||
@NotNull private final String title;
|
||||
@@ -72,7 +71,6 @@ public class PlayerListObjectiveConfiguration {
|
||||
|
||||
return new PlayerListObjectiveConfiguration(
|
||||
section,
|
||||
section.getBoolean("enabled", true),
|
||||
value,
|
||||
section.getString("fancy-value", "&7Ping: " + Placeholder.PING),
|
||||
section.getString("title", "TAB"),
|
||||
|
||||
+3
-6
@@ -138,15 +138,11 @@ public class YellowNumber extends RefreshableFeature implements JoinListener, Qu
|
||||
p.playerlistObjectiveData.disabled.set(!forced);
|
||||
return;
|
||||
}
|
||||
if (!configuration.isEnabled()) {
|
||||
p.playerlistObjectiveData.disabled.set(true);
|
||||
return;
|
||||
}
|
||||
updateVisibility(p, disabledNow);
|
||||
}
|
||||
|
||||
private boolean configuredDisabled(@NotNull TabPlayer p) {
|
||||
return !configuration.isEnabled() || disableChecker.isDisableConditionMet(p);
|
||||
return disableChecker.isDisableConditionMet(p);
|
||||
}
|
||||
|
||||
private void applyDisabledState(@NotNull TabPlayer p, boolean disabled) {
|
||||
@@ -301,8 +297,9 @@ public class YellowNumber extends RefreshableFeature implements JoinListener, Qu
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull TabPlayer player) {
|
||||
if (player.isVanished()) return;
|
||||
int value = getValue(player);
|
||||
for (TabPlayer viewer : onlinePlayers.getPlayers()) {
|
||||
setScore(viewer, player, getValue(player), player.playerlistObjectiveData.fancyValue.getFormat(viewer));
|
||||
setScore(viewer, player, value, player.playerlistObjectiveData.fancyValue.getFormat(viewer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ public class Condition {
|
||||
|
||||
List<PlaceholderReference> placeholdersInConditions = new ArrayList<>();
|
||||
PlaceholderManagerImpl manager = TAB.getInstance().getPlaceholderManager();
|
||||
if (manager == null) return; // Header / footer config conversion, the placeholder manager is not initialized yet
|
||||
placeholdersInConditions.addAll(manager.detectPlaceholders(yes).stream().map(manager::getPlaceholderReference).collect(Collectors.toList()));
|
||||
placeholdersInConditions.addAll(manager.detectPlaceholders(no).stream().map(manager::getPlaceholderReference).collect(Collectors.toList()));
|
||||
for (ConditionalExpression expression : expressions) {
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ public class PlayerPlaceholderImpl extends TabPlaceholder implements PlayerPlace
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLastReturnedValue(@Nullable TabPlayer player) {
|
||||
public synchronized String getLastReturnedValue(@Nullable TabPlayer player) {
|
||||
if (player == null) return identifier;
|
||||
String value = player.lastPlaceholderReturnedValues.get(this);
|
||||
if (value != null) return value;
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ public class RelationalPlaceholderImpl extends TabPlaceholder implements Relatio
|
||||
String value = request(viewer, target);
|
||||
String s = replacements.findReplacement(String.valueOf(value));
|
||||
viewer.lastRelationalValues.computeIfAbsent(this, v -> Collections.synchronizedMap(new WeakHashMap<>())).put(target, s);
|
||||
if (!target.isLoaded()) return; // Updated on join
|
||||
if (!target.isLoaded()) continue; // Updated on join
|
||||
for (RefreshableFeature f : reference.getUsedByFeatures()) {
|
||||
TimedCaughtTask task = new TimedCaughtTask(TAB.getInstance().getCpu(), () -> f.refresh(target, true),
|
||||
f.getFeatureName(), f.getRefreshDisplayName());
|
||||
|
||||
@@ -115,7 +115,9 @@ public abstract class TabPlaceholder implements Placeholder {
|
||||
if (PlaceholderIdentifier.isRelational(s)) continue; // Relational placeholders are handled separately
|
||||
if ((identifier.startsWith("%sync:") && ("%" + identifier.substring(6)).equals(s))) continue; // Self, but as sync variant
|
||||
PlaceholderReference nested = TAB.getInstance().getPlaceholderManager().getPlaceholderReference(s);
|
||||
nested.addParent(reference);
|
||||
if (reference != null) {
|
||||
nested.addParent(reference);
|
||||
}
|
||||
string = string.replace(s, nested.getHandle().parse(player));
|
||||
}
|
||||
return string;
|
||||
|
||||
+14
-13
@@ -49,6 +49,9 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
/** Registered teams */
|
||||
private final Map<String, Team> teams = new ConcurrentHashMap<>();
|
||||
|
||||
/** Teams indexed by entries */
|
||||
private final Map<String, Team> teamsByEntries = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized void registerObjective(@NonNull String objectiveName, @NonNull TabComponent title,
|
||||
@NonNull HealthDisplay display, @Nullable TabComponent numberFormat) {
|
||||
@@ -143,6 +146,9 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
}
|
||||
Team team = new Team(createTeam(name), name, prefix, suffix, visibility, collision, players, options, color);
|
||||
teams.put(name, team);
|
||||
for (String entry : players) {
|
||||
teamsByEntries.put(entry, team);
|
||||
}
|
||||
if (frozen) return;
|
||||
registerTeam(team);
|
||||
}
|
||||
@@ -154,6 +160,9 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
error("Tried to unregister non-existing team %s for player ", teamName);
|
||||
return;
|
||||
}
|
||||
for (String entry : team.getPlayers()) {
|
||||
teamsByEntries.remove(entry, team);
|
||||
}
|
||||
if (frozen) return;
|
||||
unregisterTeam(team);
|
||||
}
|
||||
@@ -324,14 +333,14 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
* Team name in the packet
|
||||
* @param players
|
||||
* Players in the packet
|
||||
* @return Modified collection of players
|
||||
* @return Modified list of players
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<String> onTeamPacket(int action, @NonNull String teamName, @NonNull Collection<String> players) {
|
||||
Collection<String> newList = new ArrayList<>();
|
||||
public List<String> onTeamPacket(int action, @NonNull String teamName, @NonNull Collection<String> players) {
|
||||
List<String> newList = new ArrayList<>();
|
||||
if (action == TeamAction.CREATE || action == TeamAction.ADD_PLAYER) {
|
||||
for (String entry : players) {
|
||||
Team expectedTeam = getExpectedTeam(entry);
|
||||
Team expectedTeam = teamsByEntries.get(entry);
|
||||
if (expectedTeam == null) {
|
||||
blockedTeamAdds.remove(entry);
|
||||
allowedTeamAdds.put(entry, teamName);
|
||||
@@ -350,7 +359,7 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
if (action == TeamAction.REMOVE_PLAYER) {
|
||||
// TAB does not send remove player, making checks easier
|
||||
for (String entry : players) {
|
||||
Team expectedTeam = getExpectedTeam(entry);
|
||||
Team expectedTeam = teamsByEntries.get(entry);
|
||||
if (expectedTeam != null) {
|
||||
allowedTeamAdds.remove(entry);
|
||||
blockedTeamAdds.remove(entry);
|
||||
@@ -371,14 +380,6 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
return newList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Team getExpectedTeam(@NotNull String player) {
|
||||
for (Team team : teams.values()) {
|
||||
if (team.getPlayers().contains(player)) return team;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message into anti-override log when blocking attempt to add
|
||||
* a player into a team.
|
||||
|
||||
@@ -86,7 +86,7 @@ public abstract class TrackedTabList<P extends TabPlayer> implements TabList {
|
||||
} else {
|
||||
forcedDisplayNames.remove(target.getTablistId());
|
||||
}
|
||||
if (target.getVersion().getNetworkId() < ProtocolVersion.V1_8.getNetworkId()) {
|
||||
if (player.getVersion().getNetworkId() < ProtocolVersion.V1_8.getNetworkId()) {
|
||||
return; // Display names are not supported on 1.7 and below
|
||||
}
|
||||
if (containsEntry(target.getTablistId())) {
|
||||
|
||||
Reference in new issue
Block a user