forked from Upstream/Velocitab
Improve config validator, add team collision rule setting (#152)
* Fixed tab problem on not handled servers * Fixed config validator and added collisions parameter * Fixed conversations
This commit is contained in:
parent
63ed22527b
commit
7caa185fc1
@ -137,6 +137,7 @@ public class Velocitab implements ConfigProvider, ScoreboardProvider, LoggerProv
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Velocitab getPlugin() {
|
||||
return this;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import de.exlll.configlib.NameFormatters;
|
||||
import de.exlll.configlib.YamlConfigurationProperties;
|
||||
import de.exlll.configlib.YamlConfigurations;
|
||||
import net.william278.desertwell.util.Version;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -42,8 +43,13 @@ public interface ConfigProvider {
|
||||
@NotNull
|
||||
YamlConfigurationProperties.Builder<?> YAML_CONFIGURATION_PROPERTIES = YamlConfigurationProperties.newBuilder()
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.outputNulls(true)
|
||||
.inputNulls(false)
|
||||
.setNameFormatter(NameFormatters.LOWER_UNDERSCORE);
|
||||
|
||||
@NotNull
|
||||
Velocitab getPlugin();
|
||||
|
||||
/**
|
||||
* Get the plugin settings, read from the config file
|
||||
*
|
||||
@ -72,7 +78,7 @@ public interface ConfigProvider {
|
||||
Settings.class,
|
||||
YAML_CONFIGURATION_PROPERTIES.header(Settings.CONFIG_HEADER).build()
|
||||
));
|
||||
getSettings().validateConfig();
|
||||
getSettings().validateConfig(getPlugin());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,7 +109,7 @@ public interface ConfigProvider {
|
||||
TabGroups.class,
|
||||
YAML_CONFIGURATION_PROPERTIES.header(TabGroups.CONFIG_HEADER).build()
|
||||
));
|
||||
getTabGroups().validateConfig();
|
||||
getTabGroups().validateConfig(getPlugin());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,6 +137,23 @@ public interface ConfigProvider {
|
||||
@NotNull
|
||||
Version getVelocityVersion();
|
||||
|
||||
/**
|
||||
* Saves the tab groups to the "tab_groups.yml" config file.
|
||||
* Uses the YamlConfigurations.save method to write the tab groups object to the specified config file path.
|
||||
* This method assumes that the getConfigDirectory method returns a valid directory path.
|
||||
*
|
||||
* @throws IllegalStateException if the getConfigDirectory method returns null
|
||||
* @since 1.0
|
||||
*/
|
||||
default void saveTabGroups() {
|
||||
YamlConfigurations.save(
|
||||
getConfigDirectory().resolve("tab_groups.yml"),
|
||||
TabGroups.class,
|
||||
getTabGroups(),
|
||||
YAML_CONFIGURATION_PROPERTIES.header(TabGroups.CONFIG_HEADER).build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin config directory
|
||||
*
|
||||
|
@ -19,12 +19,15 @@
|
||||
|
||||
package net.william278.velocitab.config;
|
||||
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ConfigValidator {
|
||||
|
||||
/**
|
||||
* Validates the configuration settings.
|
||||
* @throws IllegalStateException if the configuration is invalid
|
||||
*/
|
||||
void validateConfig() throws IllegalStateException;
|
||||
void validateConfig(@NotNull Velocitab plugin) throws IllegalStateException;
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ package net.william278.velocitab.config;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import de.exlll.configlib.Comment;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import net.william278.velocitab.tab.Nametag;
|
||||
@ -32,6 +31,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public record Group(
|
||||
String name,
|
||||
List<String> headers,
|
||||
@ -40,10 +40,7 @@ public record Group(
|
||||
Nametag nametag,
|
||||
List<String> servers,
|
||||
List<String> sortingPlaceholders,
|
||||
@Comment("""
|
||||
How often in milliseconds to periodically update the TAB list, including header and footer, for all users.
|
||||
If set to 0, TAB will be updated on player join/leave instead. (1s = 1000ms)
|
||||
The minimal update rate is 200ms, anything lower will automatically be set to 200ms.""")
|
||||
boolean collisions,
|
||||
int headerFooterUpdateRate,
|
||||
int placeholderUpdateRate
|
||||
) {
|
||||
|
@ -24,6 +24,7 @@ import de.exlll.configlib.Configuration;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
@ -97,7 +98,9 @@ public class Settings implements ConfigValidator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateConfig() {
|
||||
|
||||
public void validateConfig(@NotNull Velocitab plugin) {
|
||||
if (papiCacheTime < 0) {
|
||||
throw new IllegalStateException("PAPI cache time must be greater than or equal to 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,18 @@
|
||||
|
||||
package net.william278.velocitab.config;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import de.exlll.configlib.Configuration;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.tab.Nametag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@ -42,20 +47,21 @@ public class TabGroups implements ConfigValidator {
|
||||
┣╸ Information: https://william278.net/project/velocitab
|
||||
┗╸ Documentation: https://william278.net/docs/velocitab""";
|
||||
|
||||
public List<Group> groups = List.of(
|
||||
new Group(
|
||||
"default",
|
||||
List.of("&rainbow&Running Velocitab by William278"),
|
||||
List.of("[There are currently %players_online%/%max_players_online% players online](gray)"),
|
||||
"&7[%server%] &f%prefix%%username%",
|
||||
new Nametag("&f%prefix%", "&f%suffix%"),
|
||||
List.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
|
||||
List.of("%role_weight%", "%username_lower%"),
|
||||
1000,
|
||||
1000
|
||||
)
|
||||
private static final Group DEFAULT_GROUP = new Group(
|
||||
"default",
|
||||
List.of("&rainbow&Running Velocitab by William278"),
|
||||
List.of("[There are currently %players_online%/%max_players_online% players online](gray)"),
|
||||
"&7[%server%] &f%prefix%%username%",
|
||||
new Nametag("&f%prefix%", "&f%suffix%"),
|
||||
List.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
|
||||
List.of("%role_weight%", "%username_lower%"),
|
||||
false,
|
||||
1000,
|
||||
1000
|
||||
);
|
||||
|
||||
public List<Group> groups = List.of(DEFAULT_GROUP);
|
||||
|
||||
@NotNull
|
||||
public Group getGroupFromName(@NotNull String name) {
|
||||
return groups.stream()
|
||||
@ -80,12 +86,68 @@ public class TabGroups implements ConfigValidator {
|
||||
|
||||
|
||||
@Override
|
||||
public void validateConfig() {
|
||||
public void validateConfig(@NotNull Velocitab plugin) {
|
||||
if (groups.isEmpty()) {
|
||||
throw new IllegalStateException("No tab groups defined in config");
|
||||
}
|
||||
if (groups.stream().noneMatch(group -> group.name().equals("default"))) {
|
||||
throw new IllegalStateException("No default tab group defined in config");
|
||||
}
|
||||
|
||||
final Multimap<Group, String> missingKeys = getMissingKeys();
|
||||
|
||||
if (missingKeys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fixMissingKeys(plugin, missingKeys);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Multimap<Group, String> getMissingKeys() {
|
||||
final Multimap<Group, String> missingKeys = Multimaps.newSetMultimap(new HashMap<>(), HashSet::new);
|
||||
|
||||
for (Group group : groups) {
|
||||
if (group.format() == null) {
|
||||
missingKeys.put(group, "format");
|
||||
}
|
||||
if (group.nametag() == null) {
|
||||
missingKeys.put(group, "nametag");
|
||||
}
|
||||
if (group.servers() == null) {
|
||||
missingKeys.put(group, "servers");
|
||||
}
|
||||
if (group.sortingPlaceholders() == null) {
|
||||
missingKeys.put(group, "sortingPlaceholders");
|
||||
}
|
||||
}
|
||||
|
||||
return missingKeys;
|
||||
}
|
||||
|
||||
private void fixMissingKeys(@NotNull Velocitab plugin, @NotNull Multimap<Group, String> missingKeys) {
|
||||
missingKeys.forEach((group, keys) -> {
|
||||
plugin.log("Missing required key(s) " + keys + " for group " + group.name());
|
||||
plugin.log("Using default values for group " + group.name());
|
||||
|
||||
groups.remove(group);
|
||||
|
||||
group = new Group(
|
||||
group.name(),
|
||||
group.headers(),
|
||||
group.footers(),
|
||||
group.format() == null ? DEFAULT_GROUP.format() : group.format(),
|
||||
group.nametag() == null ? DEFAULT_GROUP.nametag() : group.nametag(),
|
||||
group.servers() == null ? DEFAULT_GROUP.servers() : group.servers(),
|
||||
group.sortingPlaceholders() == null ? DEFAULT_GROUP.sortingPlaceholders() : group.sortingPlaceholders(),
|
||||
group.collisions(),
|
||||
group.headerFooterUpdateRate(),
|
||||
group.placeholderUpdateRate()
|
||||
);
|
||||
|
||||
groups.add(group);
|
||||
});
|
||||
|
||||
plugin.saveTabGroups();
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
.displayName(Component.empty())
|
||||
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||
.nametagVisibility(isNametagPresent(nametag, plugin) ? NametagVisibility.ALWAYS : NametagVisibility.NEVER)
|
||||
.collisionRule(CollisionRule.ALWAYS)
|
||||
.collisionRule(tabPlayer.getGroup().collisions() ? CollisionRule.ALWAYS : CollisionRule.NEVER)
|
||||
.color(getLastColor(nametag.prefix(), plugin))
|
||||
.prefix(nametag.getPrefixComponent(plugin, tabPlayer))
|
||||
.suffix(nametag.getSuffixComponent(plugin, tabPlayer))
|
||||
@ -100,7 +100,7 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
.displayName(Component.empty())
|
||||
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||
.nametagVisibility(isNametagPresent(nametag, plugin) ? NametagVisibility.ALWAYS : NametagVisibility.NEVER)
|
||||
.collisionRule(CollisionRule.ALWAYS)
|
||||
.collisionRule(tabPlayer.getGroup().collisions() ? CollisionRule.ALWAYS : CollisionRule.NEVER)
|
||||
.color(getLastColor(nametag.prefix(), plugin))
|
||||
.prefix(nametag.getPrefixComponent(plugin, tabPlayer))
|
||||
.suffix(nametag.getSuffixComponent(plugin, tabPlayer));
|
||||
|
@ -44,6 +44,8 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
private int headerIndex = 0;
|
||||
private int footerIndex = 0;
|
||||
private Component lastDisplayName;
|
||||
private Component lastHeader;
|
||||
private Component lastFooter;
|
||||
private String teamName;
|
||||
@Nullable
|
||||
@Setter
|
||||
@ -127,7 +129,11 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
|
||||
public CompletableFuture<Void> sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
|
||||
return tabList.getHeader(this).thenCompose(header -> tabList.getFooter(this)
|
||||
.thenAccept(footer -> player.sendPlayerListHeaderAndFooter(header, footer)));
|
||||
.thenAccept(footer -> {
|
||||
lastHeader = header;
|
||||
lastFooter = footer;
|
||||
player.sendPlayerListHeaderAndFooter(header, footer);
|
||||
}));
|
||||
}
|
||||
|
||||
public void incrementIndexes() {
|
||||
|
@ -31,8 +31,10 @@ import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.config.Group;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -76,12 +78,24 @@ public class TabListListener {
|
||||
// If the server is not in a group, use fallback.
|
||||
// If fallback is disabled, permit the player to switch excluded servers without a header or footer override
|
||||
if (isDefault && !plugin.getSettings().isFallbackEnabled()) {
|
||||
final Component header = event.getPlayer().getPlayerListHeader();
|
||||
final Component footer = event.getPlayer().getPlayerListFooter();
|
||||
final Optional<TabPlayer> tabPlayer = tabList.getTabPlayer(joined);
|
||||
if (tabPlayer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final Component header = tabPlayer.get().getLastHeader();
|
||||
final Component footer = tabPlayer.get().getLastFooter();
|
||||
final Component displayName = tabPlayer.get().getLastDisplayName();
|
||||
|
||||
plugin.getServer().getScheduler().buildTask(plugin, () -> {
|
||||
if (header.equals(event.getPlayer().getPlayerListHeader()) && footer.equals(event.getPlayer().getPlayerListFooter())) {
|
||||
event.getPlayer().sendPlayerListHeaderAndFooter(header, footer);
|
||||
event.getPlayer().getCurrentServer().ifPresent(serverConnection ->
|
||||
serverConnection.getServer().getPlayersConnected().forEach(player ->
|
||||
player.getTabList().getEntry(joined.getUniqueId()).ifPresent(entry -> {
|
||||
if (entry.getDisplayNameComponent().isPresent() && entry.getDisplayNameComponent().get().equals(displayName)) {
|
||||
entry.setDisplayName(Component.text(joined.getUsername()));
|
||||
}
|
||||
})));
|
||||
}
|
||||
}).delay(500, TimeUnit.MILLISECONDS).schedule();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user