Add custom tab

This commit is contained in:
Alessandro Proto 2024-10-25 11:43:33 +02:00
parent 5530b3b430
commit 325e52bc19
4 changed files with 125 additions and 0 deletions

View file

@ -7,6 +7,7 @@ import cc.reconnected.server.events.PlayerWelcome;
import cc.reconnected.server.events.Ready;
import cc.reconnected.server.http.ServiceServer;
import cc.reconnected.server.struct.ServerPosition;
import cc.reconnected.server.tablist.TabList;
import cc.reconnected.server.trackers.AfkTracker;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@ -14,6 +15,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyori.adventure.platform.fabric.FabricServerAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
@ -84,6 +86,16 @@ public class RccServer implements ModInitializer {
INSTANCE = this;
}
private volatile FabricServerAudiences adventure;
public FabricServerAudiences adventure() {
FabricServerAudiences ret = this.adventure;
if (ret == null) {
throw new IllegalStateException("Tried to access Adventure without a running server!");
}
return ret;
}
public static final ConcurrentHashMap<UUID, ConcurrentLinkedDeque<TeleportAskCommand.TeleportRequest>> teleportRequests = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<UUID, ServerPosition> lastPlayerPositions = new ConcurrentHashMap<>();
@ -91,7 +103,11 @@ public class RccServer implements ModInitializer {
public void onInitialize() {
LOGGER.info("Starting rcc-server");
ServerLifecycleEvents.SERVER_STARTING.register(server -> this.adventure = FabricServerAudiences.of(server));
ServerLifecycleEvents.SERVER_STOPPED.register(server -> this.adventure = null);
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
RccCommand.register(dispatcher, registryAccess, environment);
AfkCommand.register(dispatcher, registryAccess, environment);
TellCommand.register(dispatcher, registryAccess, environment);
ReplyCommand.register(dispatcher, registryAccess, environment);
@ -104,6 +120,8 @@ public class RccServer implements ModInitializer {
GodCommand.register(dispatcher, registryAccess, environment);
});
TabList.register();
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
luckPerms = LuckPermsProvider.get();
afkTracker = new AfkTracker();

View file

@ -2,6 +2,9 @@ package cc.reconnected.server;
import io.wispforest.owo.config.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@Config(name = "rcc-server-config", wrapperName = "RccServerConfig")
public class RccServerConfigModel {
public boolean enableHttpApi = true;
@ -16,4 +19,13 @@ public class RccServerConfigModel {
public String tellMessageSpy = "\uD83D\uDC41 <gray>[<source> → <target>]</gray> <message>";
public int teleportRequestTimeout = 120;
public boolean enableTabList = true;
public ArrayList<String> tabHeader = new ArrayList<>(List.of(
"<gradient:#DEDE6C:#CC4C4C:{phase}><st> </st></gradient>"
));
public ArrayList<String> tabFooter = new ArrayList<>(List.of(
"<gradient:#DEDE6C:#CC4C4C:{phase}><st> </st></gradient>"
));
}

View file

@ -0,0 +1,36 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import com.mojang.brigadier.CommandDispatcher;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import static net.minecraft.server.command.CommandManager.literal;
public class RccCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
var rootCommand = literal("rcc")
.requires(Permissions.require("rcc.command.afk", 3))
.then(literal("reload")
.executes(context -> {
context.getSource().sendFeedback(() -> Text.of("Reloading RCC config..."), true);
try {
RccServer.CONFIG.load();
} catch(Exception e) {
RccServer.LOGGER.error("Failed to load RCC config", e);
context.getSource().sendFeedback(() -> Text.of("Failed to load RCC config. Check console for more info."), true);
return 1;
}
context.getSource().sendFeedback(() -> Text.of("Reloaded RCC config"), true);
return 1;
}));
dispatcher.register(rootCommand);
}
}

View file

@ -0,0 +1,59 @@
package cc.reconnected.server.tablist;
import cc.reconnected.server.RccServer;
import eu.pb4.placeholders.api.PlaceholderContext;
import eu.pb4.placeholders.api.Placeholders;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
import net.minecraft.text.Text;
public class TabList {
public static void register() {
if (!RccServer.CONFIG.enableTabList())
return;
ServerTickEvents.END_SERVER_TICK.register(server -> {
var phase = (Math.sin((server.getTicks() * Math.PI) / 20) + 1) / 2d;
var minimessage = MiniMessage.miniMessage();
server.getPlayerManager().getPlayerList().forEach(player -> {
var playerContext = PlaceholderContext.of(player);
Component headerComponent = Component.empty();
for (int i = 0; i < RccServer.CONFIG.tabHeader().size(); i++) {
var line = RccServer.CONFIG.tabHeader().get(i);
line = line.replace("{phase}", String.valueOf(phase));
if (i > 0) {
headerComponent = headerComponent.appendNewline();
}
headerComponent = headerComponent.append(minimessage.deserialize(line));
}
Component footerComponent = Component.empty();
for (int i = 0; i < RccServer.CONFIG.tabFooter().size(); i++) {
var line = RccServer.CONFIG.tabFooter().get(i);
line = line.replace("{phase}", String.valueOf(phase));
if (i > 0) {
footerComponent = footerComponent.appendNewline();
}
footerComponent = footerComponent.append(minimessage.deserialize(line));
}
var parsedHeader = Placeholders.parseText(toText(headerComponent), playerContext);
var parsedFooter = Placeholders.parseText(toText(footerComponent), playerContext);
var audience = RccServer.getInstance().adventure().player(player.getUuid());
audience.sendPlayerListHeaderAndFooter(parsedHeader, parsedFooter);
});
});
}
public static Text toText(Component component) {
var json = JSONComponentSerializer.json().serialize(component);
return Text.Serializer.fromJson(json);
}
}