port: compat layer

- add compat/Adventure (BukkitAudiences lifecycle with legacy fallback)
- add compat/Msg (send/broadcast/actionBar/title/legacy/plain/displayName/playerListName/kick/itemMeta/inventoryTitle)
- wire Adventure.init/close in LoChat onEnable/onDisable
- remove unused reflection-based utils/platform/AdventureManager
This commit is contained in:
loki5512344 2026-09-20 16:18:40 +02:00
parent dd725ad255
commit 88e4e68cca
Signed by: boba
GPG key ID: 253067914055423B
4 changed files with 227 additions and 96 deletions

View file

@ -47,7 +47,8 @@ public final class LoChat extends JavaPlugin {
@Override
public void onEnable() {
instance = this;
getLogger().info("Загрузка LoChat v" + getPluginMeta().getVersion() + " для Paper/Folia " + Bukkit.getMinecraftVersion() + "...");
com.loki.lochat.compat.Adventure.init(this);
getLogger().info("Загрузка LoChat v" + getDescription().getVersion() + " для Spigot " + Bukkit.getBukkitVersion() + "...");
// Инициализация
initializer = new PluginInitializer(this);
@ -95,6 +96,7 @@ public final class LoChat extends JavaPlugin {
@Override
public void onDisable() {
shutdown.shutdown(serviceRegistry, autoMessageManager, discordIntegration, gradientModule);
com.loki.lochat.compat.Adventure.close();
}
public void reload() {

View file

@ -0,0 +1,50 @@
package com.loki.lochat.compat;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
/**
* Holds BukkitAudiences for Spigot. Falls back to legacy text if creation fails.
*/
public final class Adventure {
private static BukkitAudiences audiences;
private static boolean fallback;
private Adventure() {
}
public static void init(Plugin plugin) {
try {
audiences = BukkitAudiences.create(plugin);
fallback = false;
} catch (Throwable t) {
audiences = null;
fallback = true;
plugin.getLogger().warning(
"[LoChat] BukkitAudiences init failed, falling back to legacy text: " + t.getMessage());
}
}
public static void close() {
if (audiences != null) {
try {
audiences.close();
} catch (Exception ignored) {
}
audiences = null;
}
fallback = false;
}
@Nullable
public static BukkitAudiences audiences() {
return audiences;
}
public static boolean isFallback() {
return fallback || audiences == null;
}
}

View file

@ -0,0 +1,174 @@
package com.loki.lochat.compat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.kyori.adventure.title.Title;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.meta.ItemMeta;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
/**
* Static helpers for sending Adventure components on Spigot via BukkitAudiences.
* Falls back to legacy String when audiences not available.
*/
public final class Msg {
private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.builder()
.character('§')
.hexColors()
.useUnusualXRepeatedCharacterHexFormat()
.build();
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
private Msg() {
}
public static void send(CommandSender sender, Component component) {
if (sender == null || component == null) {
return;
}
if (!Adventure.isFallback() && Adventure.audiences() != null) {
try {
Adventure.audiences().sender(sender).sendMessage(component);
return;
} catch (Throwable ignored) {
}
}
sender.sendMessage(legacy(component));
}
public static void send(Collection<? extends CommandSender> senders, Component component) {
if (senders == null || component == null) {
return;
}
for (CommandSender s : senders) {
send(s, component);
}
}
public static void broadcast(Component component) {
if (component == null) {
return;
}
if (!Adventure.isFallback() && Adventure.audiences() != null) {
try {
Adventure.audiences().all().sendMessage(component);
return;
} catch (Throwable ignored) {
}
}
String legacy = legacy(component);
Bukkit.getOnlinePlayers().forEach(p -> p.sendMessage(legacy));
Bukkit.getConsoleSender().sendMessage(legacy);
}
public static void actionBar(Player player, Component component) {
if (player == null || component == null) {
return;
}
if (!Adventure.isFallback() && Adventure.audiences() != null) {
try {
Adventure.audiences().player(player).sendActionBar(component);
return;
} catch (Throwable ignored) {
}
}
// Fallback: send as chat
player.sendMessage(legacy(component));
}
public static void title(Player player, Component title, Component subtitle, Title.Times times) {
if (player == null) {
return;
}
Component t = title != null ? title : Component.empty();
Component s = subtitle != null ? subtitle : Component.empty();
Title.Times tm = times != null ? times : Title.Times.times(
Duration.ofMillis(500), Duration.ofMillis(3000), Duration.ofMillis(500));
if (!Adventure.isFallback() && Adventure.audiences() != null) {
try {
Adventure.audiences().player(player).showTitle(Title.title(t, s, tm));
return;
} catch (Throwable ignored) {
}
}
// Fallback via Bukkit API (legacy strings, 1.20.1 supports titles)
player.sendTitle(legacy(t), legacy(s),
(int) (tm.fadeIn().toMillis() / 50),
(int) (tm.stay().toMillis() / 50),
(int) (tm.fadeOut().toMillis() / 50));
}
public static String legacy(Component component) {
if (component == null) {
return "";
}
return LEGACY.serialize(component);
}
public static String plain(Component component) {
if (component == null) {
return "";
}
return PLAIN.serialize(component);
}
public static void setDisplayName(Player player, Component component) {
if (player == null) {
return;
}
player.setDisplayName(component == null ? "" : legacy(component));
}
public static void setPlayerListName(Player player, Component component) {
if (player == null) {
return;
}
player.setPlayerListName(component == null ? "" : legacy(component));
}
public static void kick(Player player, Component reason) {
if (player == null) {
return;
}
String msg = reason == null ? "" : legacy(reason);
// Must be on main thread; caller should ensure via scheduler if needed
if (Bukkit.isPrimaryThread()) {
player.kickPlayer(msg);
} else {
Bukkit.getScheduler().runTask(
Bukkit.getPluginManager().getPlugin("LoChat"), () -> player.kickPlayer(msg));
}
}
public static void itemName(ItemMeta meta, Component name) {
if (meta == null) {
return;
}
meta.setDisplayName(name == null ? null : legacy(name));
}
public static void itemLore(ItemMeta meta, List<Component> lore) {
if (meta == null) {
return;
}
if (lore == null) {
meta.setLore(null);
return;
}
List<String> lines = lore.stream().map(Msg::legacy).toList();
meta.setLore(lines);
}
public static String inventoryTitle(Component title) {
return legacy(title);
}
}

View file

@ -1,95 +0,0 @@
package com.loki.lochat.utils.platform;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
/**
* Менеджер для работы с Adventure API на Arclight/Forge серверах
*/
public class AdventureManager {
private AdventureManager() {
}
private static boolean adventureAvailable = false;
private static Object audiences;
public static void init(Plugin plugin) {
try {
// Проверяем наличие Adventure API
Class.forName("net.kyori.adventure.text.Component");
Class<?> audiencesClass = Class.forName("net.kyori.adventure.platform.bukkit.BukkitAudiences");
// Создаем BukkitAudiences через рефлексию
Object audiencesInstance = audiencesClass.getMethod("create", Plugin.class).invoke(null, plugin);
audiences = audiencesInstance;
adventureAvailable = true;
plugin.getLogger().info("Adventure API обнаружен - используем нативную поддержку");
} catch (Exception e) {
adventureAvailable = false;
plugin.getLogger().info("Adventure API не найден - используем legacy режим");
}
}
public static void shutdown() {
if (audiences != null && adventureAvailable) {
try {
audiences.getClass().getMethod("close").invoke(audiences);
} catch (Exception e) {
// Игнорируем ошибки при закрытии
}
audiences = null;
}
adventureAvailable = false;
}
public static void sendMessage(CommandSender sender, Object message) {
if (adventureAvailable && audiences != null) {
try {
// Используем Adventure API
Object senderAudience = audiences.getClass().getMethod("sender", CommandSender.class).invoke(audiences, sender);
senderAudience.getClass()
.getMethod("sendMessage", Class.forName("net.kyori.adventure.text.Component"))
.invoke(senderAudience, message);
return;
} catch (Exception e) {
// Fallback на legacy
}
}
// Legacy fallback
if (message instanceof String) {
sender.sendMessage((String) message);
} else {
sender.sendMessage(message.toString());
}
}
public static void sendMessage(Player player, Object message) {
if (adventureAvailable && audiences != null) {
try {
// Используем Adventure API
Object playerAudience = audiences.getClass().getMethod("player", Player.class).invoke(audiences, player);
playerAudience.getClass()
.getMethod("sendMessage", Class.forName("net.kyori.adventure.text.Component"))
.invoke(playerAudience, message);
return;
} catch (Exception e) {
// Fallback на legacy
}
}
// Legacy fallback
if (message instanceof String) {
player.sendMessage((String) message);
} else {
player.sendMessage(message.toString());
}
}
public static boolean isAdventureAvailable() {
return adventureAvailable;
}
}