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 |
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.3"><title>Release: 6.1.3</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.3</text><text x="715" y="150" fill-opacity=".3" textLength="290">6.1.3</text></g><text x="715" y="140" textLength="290">6.1.3</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.3"
|
||||
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.
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "6.2.0"
|
||||
version = "6.2.1"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
|
||||
+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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -290,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-1
@@ -297,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
+11
-10
@@ -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);
|
||||
}
|
||||
@@ -331,7 +340,7 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
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