添加更多参数化支持
This commit is contained in:
parent
20330df2a9
commit
119066dd6d
|
|
@ -49,7 +49,7 @@ mod_name=Leisure Time Dock Mod
|
|||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=MIT
|
||||
# The mod version. See https://semver.org/
|
||||
mod_version=1.1.2
|
||||
mod_version=1.2.
|
||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||
# This should match the base package used for the mod sources.
|
||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import net.minecraftforge.event.server.ServerStoppedEvent;
|
|||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.IExtensionPoint;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
|
@ -154,5 +155,21 @@ public class CrossTeleportMod {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The type Mod info.
|
||||
*/
|
||||
public static class ModInfo {
|
||||
/**
|
||||
* The constant VERSION.
|
||||
*/
|
||||
public static final String VERSION;
|
||||
static {
|
||||
// 从 ModList 获取当前 ModContainer 的元数据
|
||||
VERSION = ModList.get()
|
||||
.getModContainerById(MOD_ID)
|
||||
.map(c -> c.getModInfo().getVersion().toString())
|
||||
.orElse("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.Checkbox;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
|
@ -16,7 +17,9 @@ import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -26,11 +29,22 @@ public class CrossServerGui extends Screen {
|
|||
private static final ResourceLocation CHANNEL_ID = new ResourceLocation(CrossTeleportMod.MOD_ID, "teleport");
|
||||
private static final ResourceLocation LOGO_TEXTURE = new ResourceLocation(CrossTeleportMod.MOD_ID, "textures/ltd_logo.png");
|
||||
|
||||
// 开发模式标志
|
||||
private static final boolean DEV_MODE = !FMLEnvironment.production;
|
||||
|
||||
// 存储组件引用,以便在渲染时控制顺序
|
||||
private Checkbox enableCrCheckBox;
|
||||
private Checkbox enablePiCheckBox;
|
||||
private Button closeButton;
|
||||
private Button devTestButton;
|
||||
private Button devToggleDebugButton;
|
||||
private ServerSelectionList serverList;
|
||||
private EditBox searchBox;
|
||||
|
||||
// 开发测试数据
|
||||
private long lastUpdateTime = 0;
|
||||
private int serverCount = 0;
|
||||
private boolean showDevInfo = true;
|
||||
|
||||
public CrossServerGui() {
|
||||
super(TITLE);
|
||||
|
|
@ -38,11 +52,55 @@ public class CrossServerGui extends Screen {
|
|||
|
||||
@Override
|
||||
protected void init() {
|
||||
// 先添加按钮和复选框(它们应该渲染在列表上方)
|
||||
// 先添加搜索框
|
||||
initSearchBox();
|
||||
|
||||
// 再添加按钮和复选框(它们应该渲染在列表上方)
|
||||
initButtons();
|
||||
|
||||
// 再添加列表(它应该渲染在下方)
|
||||
// 开发模式按钮
|
||||
if (DEV_MODE) {
|
||||
initDevButtons();
|
||||
}
|
||||
|
||||
// 最后添加列表(它应该渲染在下方)
|
||||
initServerList();
|
||||
|
||||
// 更新服务器计数
|
||||
serverCount = CrossServerConfigManager.INSTANCE.getServers().size();
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
|
||||
// 设置初始焦点
|
||||
this.setInitialFocus(searchBox);
|
||||
}
|
||||
|
||||
private void initSearchBox() {
|
||||
int centerX = width / 2;
|
||||
|
||||
// 创建搜索框
|
||||
searchBox = new EditBox(
|
||||
this.font,
|
||||
centerX - 100, // x位置
|
||||
50, // y位置(标题下方)
|
||||
200, // 宽度
|
||||
16, // 高度
|
||||
Component.translatable("ltd.mod.client.menu.search")
|
||||
);
|
||||
searchBox.setMaxLength(50);
|
||||
searchBox.setVisible(true);
|
||||
searchBox.setFocused(false);
|
||||
searchBox.setHint(Component.translatable("ltd.mod.client.menu.search"));
|
||||
// 启用文本框的输入
|
||||
searchBox.setCanLoseFocus(true);
|
||||
|
||||
// 设置文本变化监听器
|
||||
searchBox.setResponder(searchText -> {
|
||||
if (serverList != null) {
|
||||
serverList.setSearchFilter(searchText);
|
||||
}
|
||||
});
|
||||
|
||||
addRenderableWidget(searchBox);
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
|
|
@ -83,31 +141,76 @@ public class CrossServerGui extends Screen {
|
|||
addRenderableWidget(closeButton);
|
||||
}
|
||||
|
||||
private void initDevButtons() {
|
||||
int centerX = width / 2;
|
||||
int topY = 45;
|
||||
|
||||
// 开发测试按钮 - 重新加载配置
|
||||
devTestButton = Button.builder(
|
||||
Component.literal("§6[Dev] Reload"),
|
||||
button -> {
|
||||
CrossServerConfigManager.INSTANCE.reloadAll();
|
||||
refreshServerList();
|
||||
serverCount = CrossServerConfigManager.INSTANCE.getServers().size();
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
)
|
||||
.bounds(centerX + 120, topY, 70, 16)
|
||||
.build();
|
||||
addRenderableWidget(devTestButton);
|
||||
|
||||
// 开发测试按钮 - 切换调试信息显示
|
||||
devToggleDebugButton = Button.builder(
|
||||
Component.literal("§7[Dev] " + (showDevInfo ? "Hide" : "Show") + " Debug"),
|
||||
button -> {
|
||||
showDevInfo = !showDevInfo;
|
||||
button.setMessage(Component.literal("§7[Dev] " + (showDevInfo ? "Hide" : "Show") + " Debug"));
|
||||
}
|
||||
)
|
||||
.bounds(centerX + 195, topY, 80, 16)
|
||||
.build();
|
||||
addRenderableWidget(devToggleDebugButton);
|
||||
}
|
||||
|
||||
private void initServerList() {
|
||||
int screenWidth = this.width;
|
||||
int screenHeight = this.height;
|
||||
|
||||
|
||||
// 创建服务器列表,但尺寸要避开按钮区域
|
||||
// 创建服务器列表,调整位置为搜索框下方
|
||||
serverList = new ServerSelectionList(
|
||||
this,
|
||||
Minecraft.getInstance(),
|
||||
screenWidth,
|
||||
screenHeight,
|
||||
48, // X位置
|
||||
height - 64, // Y位置
|
||||
70, // Y起始位置(搜索框下方)
|
||||
height - 64, // Y结束位置
|
||||
36, // 条目高度
|
||||
CrossServerConfigManager.INSTANCE.getServers()
|
||||
CrossServerConfigManager.INSTANCE.getServers()
|
||||
);
|
||||
|
||||
// 设置列表属性
|
||||
serverList.setRenderBackground(true);
|
||||
serverList.setRenderBackground(false);
|
||||
serverList.setRenderTopAndBottom(true);
|
||||
|
||||
|
||||
addRenderableWidget(serverList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新服务器列表
|
||||
*/
|
||||
private void refreshServerList() {
|
||||
if (serverList != null) {
|
||||
// 移除旧的列表
|
||||
this.removeWidget(serverList);
|
||||
// 创建新的列表
|
||||
initServerList();
|
||||
// 如果有搜索文本,重新应用
|
||||
if (searchBox != null && !searchBox.getValue().isEmpty()) {
|
||||
serverList.setSearchFilter(searchBox.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||
|
||||
|
|
@ -119,8 +222,12 @@ public class CrossServerGui extends Screen {
|
|||
serverList.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
// 渲染搜索框
|
||||
if (searchBox != null) {
|
||||
searchBox.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
// 然后渲染按钮和复选框(在上层)
|
||||
// 手动调用按钮的render方法,确保它们在最上面
|
||||
if (enableCrCheckBox != null) {
|
||||
enableCrCheckBox.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
|
@ -130,12 +237,167 @@ public class CrossServerGui extends Screen {
|
|||
if (closeButton != null) {
|
||||
closeButton.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
// 渲染开发按钮
|
||||
if (DEV_MODE) {
|
||||
if (devTestButton != null) {
|
||||
devTestButton.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
if (devToggleDebugButton != null) {
|
||||
devToggleDebugButton.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
renderLogo(guiGraphics);
|
||||
|
||||
// 渲染开发调试信息(不省略任何信息)
|
||||
if (DEV_MODE && showDevInfo) {
|
||||
renderDevInfo(guiGraphics);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染开发调试信息 - 完整显示所有信息
|
||||
*/
|
||||
private void renderDevInfo(@NotNull GuiGraphics guiGraphics) {
|
||||
int x = 10;
|
||||
int y = 10;
|
||||
int lineHeight = 11;
|
||||
int color = 0x55FF55; // 绿色
|
||||
int textColor = 0xFFFFFF; // 白色
|
||||
|
||||
// 显示基本信息
|
||||
guiGraphics.drawString(this.font, "§a[Dev Mode] §7- Debug Information", x, y, color);
|
||||
y += lineHeight;
|
||||
|
||||
guiGraphics.drawString(this.font, "§7Servers: §f" + serverCount, x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
guiGraphics.drawString(this.font, "§7Players Online: §f" + getOnlinePlayerCount(), x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
guiGraphics.drawString(this.font, "§7Current Server: §f" + getCurrentServerName(), x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示服务器列表详细信息 - 完整显示所有服务器
|
||||
Map<String, String> servers = CrossServerConfigManager.INSTANCE.getServers();
|
||||
if (!servers.isEmpty()) {
|
||||
guiGraphics.drawString(this.font, "§7Server List (§f" + servers.size() + "§7):", x, y, color);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示所有服务器,不省略
|
||||
int count = 0;
|
||||
for (Map.Entry<String, String> entry : servers.entrySet()) {
|
||||
String display = "§8 " + (count + 1) + ". §f" + entry.getKey() + " §7→ §e" + entry.getValue();
|
||||
// 如果文本太长,截断但保留完整信息
|
||||
if (display.length() > 256) {
|
||||
display = display.substring(0, 47) + "...";
|
||||
}
|
||||
guiGraphics.drawString(this.font, display, x + 5, y, 0xAAAAAA);
|
||||
y += lineHeight;
|
||||
count++;
|
||||
|
||||
// 防止信息超出屏幕
|
||||
if (y > height - 100) {
|
||||
guiGraphics.drawString(this.font, "§8... and more (scroll to see all)", x + 5, y, 0x888888);
|
||||
y += lineHeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示内存信息
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long totalMemory = runtime.totalMemory() / 1024 / 1024;
|
||||
long freeMemory = runtime.freeMemory() / 1024 / 1024;
|
||||
long usedMemory = totalMemory - freeMemory;
|
||||
long maxMemory = runtime.maxMemory() / 1024 / 1024;
|
||||
guiGraphics.drawString(this.font, "§7Memory: §f" + usedMemory + "MB §7used / §f" + totalMemory + "MB §7allocated / §f" + maxMemory + "MB §7max", x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示FPS信息
|
||||
try {
|
||||
int fps = Minecraft.getInstance().getFps();
|
||||
String fpsColor = fps >= 60 ? "§a" : (fps >= 30 ? "§e" : "§c");
|
||||
guiGraphics.drawString(this.font, "§7FPS: " + fpsColor + fps, x, y, textColor);
|
||||
y += lineHeight;
|
||||
} catch (Exception e) {
|
||||
// 忽略
|
||||
}
|
||||
|
||||
// 显示Java版本信息
|
||||
guiGraphics.drawString(this.font, "§7Java: §f" + System.getProperty("java.version"), x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示Minecraft版本
|
||||
guiGraphics.drawString(this.font, "§7MC Version: §f" + Minecraft.getInstance().getVersionType(), x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示Mod版本
|
||||
guiGraphics.drawString(this.font, "§7Mod Version: §f" + CrossTeleportMod.ModInfo.VERSION, x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示最后更新时间
|
||||
guiGraphics.drawString(this.font, "§7Last Update: §f" + (System.currentTimeMillis() - lastUpdateTime) + "ms ago", x, y, textColor);
|
||||
y += lineHeight;
|
||||
|
||||
// 显示搜索框状态
|
||||
if (searchBox != null) {
|
||||
String searchText = searchBox.getValue();
|
||||
if (!searchText.isEmpty()) {
|
||||
guiGraphics.drawString(this.font, "§7Search: §f\"" + searchText + "\" §7(§f" + searchText.length() + "§7 chars)", x, y, textColor);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示调试提示
|
||||
guiGraphics.drawString(this.font, "§8Press 'Dev Toggle' to hide this panel", x, y, 0x888888);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前服务器名称
|
||||
*/
|
||||
private String getCurrentServerName() {
|
||||
try {
|
||||
if (Minecraft.getInstance().getCurrentServer() != null) {
|
||||
return Minecraft.getInstance().getCurrentServer().name;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略
|
||||
}
|
||||
return "SinglePlayer/Local";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线玩家数量
|
||||
*/
|
||||
private int getOnlinePlayerCount() {
|
||||
try {
|
||||
if (Minecraft.getInstance().getConnection() != null) {
|
||||
return Minecraft.getInstance().getConnection().getOnlinePlayers().size();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略错误
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
// 先检查按钮点击
|
||||
// 先检查搜索框点击 - 让搜索框获得焦点
|
||||
if (searchBox != null) {
|
||||
boolean clicked = searchBox.mouseClicked(mouseX, mouseY, button);
|
||||
if (clicked) {
|
||||
// 点击搜索框时,取消列表的焦点,并确保搜索框获得焦点
|
||||
if (serverList != null) {
|
||||
serverList.setFocused(false);
|
||||
}
|
||||
searchBox.setFocused(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 再检查按钮点击
|
||||
if (enableCrCheckBox != null && enableCrCheckBox.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -145,15 +407,57 @@ public class CrossServerGui extends Screen {
|
|||
if (closeButton != null && closeButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
if (DEV_MODE) {
|
||||
if (devTestButton != null && devTestButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
if (devToggleDebugButton != null && devToggleDebugButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 再检查列表点击
|
||||
// 最后检查列表点击
|
||||
if (serverList != null && serverList.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 点击其他地方时,取消搜索框焦点
|
||||
if (searchBox != null && searchBox.isFocused()) {
|
||||
searchBox.setFocused(false);
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
// ESC键关闭GUI
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
this.onClose();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 优先让搜索框处理按键(如果搜索框有焦点)
|
||||
if (searchBox != null && searchBox.isFocused()) {
|
||||
if (searchBox.keyPressed(keyCode, scanCode, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean charTyped(char codePoint, int modifiers) {
|
||||
// 优先让搜索框处理字符输入
|
||||
if (searchBox != null && searchBox.isFocused()) {
|
||||
if (searchBox.charTyped(codePoint, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.charTyped(codePoint, modifiers);
|
||||
}
|
||||
|
||||
void sendCustomPayload(String message) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.getConnection() != null) {
|
||||
|
|
@ -164,11 +468,11 @@ public class CrossServerGui extends Screen {
|
|||
}
|
||||
|
||||
private void renderLogo(@NotNull GuiGraphics guiGraphics) {
|
||||
int logoWidth = 64; // 缩小Logo,为列表腾出空间
|
||||
int logoWidth = 64;
|
||||
int logoHeight = 64;
|
||||
|
||||
int x = (this.width - logoWidth - font.width(this.title.getString()) * 2) / 2;
|
||||
int y = -5; // 更靠近顶部
|
||||
int y = -5;
|
||||
guiGraphics.blit(LOGO_TEXTURE, x, y, 0, 0, logoWidth, logoHeight, logoWidth, logoHeight);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.leisuretimedock.crossmod.client.gui;
|
||||
|
||||
import com.leisuretimedock.crossmod.client.KeyBindingHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
|
|
@ -7,25 +8,272 @@ import net.minecraft.client.gui.components.ObjectSelectionList;
|
|||
import net.minecraft.network.chat.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ServerSelectionList extends ObjectSelectionList<ServerSelectionList.ServerEntry> {
|
||||
private final CrossServerGui parentScreen;
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(\\w+)\\}");
|
||||
private static final Pattern FORMAT_PATTERN = Pattern.compile("%([dsf])");
|
||||
|
||||
// 存储所有服务器条目用于过滤
|
||||
private final List<ServerEntry> allEntries = new ArrayList<>();
|
||||
private String searchFilter = "";
|
||||
|
||||
public ServerSelectionList(CrossServerGui parent, Minecraft mc, int width, int height, int y0, int y1, int itemHeight, @NotNull Map<String, String> servers) {
|
||||
super(mc, width, height, y0, y1, itemHeight);
|
||||
this.parentScreen = parent;
|
||||
|
||||
// 添加服务器条目
|
||||
// 创建所有条目
|
||||
if (servers.isEmpty()) {
|
||||
this.addEntry(new ServerEntry(Component.translatable("ltd.mod.client.menu.button.no_servers"), null, parentScreen));
|
||||
ServerEntry entry = new ServerEntry(Component.translatable("ltd.mod.client.menu.button.no_servers"), null, parentScreen);
|
||||
allEntries.add(entry);
|
||||
this.addEntry(entry);
|
||||
} else {
|
||||
servers.forEach((server_name, translate_key) -> {
|
||||
this.addEntry(new ServerEntry(Component.translatable(translate_key), server_name, parentScreen));
|
||||
servers.forEach((serverId, translateKey) -> {
|
||||
Component displayName = formatComponent(translateKey, serverId);
|
||||
ServerEntry entry = new ServerEntry(displayName, serverId, parentScreen);
|
||||
allEntries.add(entry);
|
||||
this.addEntry(entry);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置搜索过滤器
|
||||
*/
|
||||
public void setSearchFilter(String filterText) {
|
||||
this.searchFilter = filterText == null ? "" : filterText.trim().toLowerCase();
|
||||
refreshEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表显示(应用当前过滤)
|
||||
*/
|
||||
private void refreshEntries() {
|
||||
this.clearEntries();
|
||||
|
||||
if (searchFilter.isEmpty()) {
|
||||
// 无过滤,显示所有条目
|
||||
allEntries.forEach(this::addEntry);
|
||||
} else {
|
||||
// 过滤条目
|
||||
boolean hasMatch = false;
|
||||
for (ServerEntry entry : allEntries) {
|
||||
if (entry.serverId == null) {
|
||||
// "无服务器"条目只在没有其他条目时显示
|
||||
continue;
|
||||
}
|
||||
String displayText = entry.displayName.getString().toLowerCase();
|
||||
String serverId = entry.serverId.toLowerCase();
|
||||
if (displayText.contains(searchFilter) || serverId.contains(searchFilter)) {
|
||||
this.addEntry(entry);
|
||||
hasMatch = true;
|
||||
}
|
||||
}
|
||||
// 如果没有匹配的条目,显示提示
|
||||
if (!hasMatch && !allEntries.isEmpty()) {
|
||||
Component noMatch = Component.literal("§7没有匹配的服务器: " + searchFilter);
|
||||
this.addEntry(new ServerEntry(noMatch, null, parentScreen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Component formatComponent(String input, String serverId) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return Component.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查是否包含格式化参数
|
||||
if (input.contains("?")) {
|
||||
String[] parts = input.split("\\?", 2);
|
||||
String key = parts[0];
|
||||
String paramString = parts[1];
|
||||
|
||||
// 解析参数
|
||||
Map<String, String> params = parseParameters(paramString);
|
||||
|
||||
// 获取翻译文本
|
||||
String template = Component.translatable(key).getString();
|
||||
|
||||
// 先替换 {placeholder} 格式
|
||||
String formatted = replacePlaceholders(template, params, serverId);
|
||||
|
||||
// 再处理 %s, %d, %f 格式
|
||||
formatted = handleStringFormat(formatted, params, serverId);
|
||||
|
||||
return Component.literal(formatted);
|
||||
} else {
|
||||
// 没有参数,直接翻译
|
||||
return Component.translatable(input);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 出错时回退到原始文本
|
||||
return Component.literal(input);
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull Map<String, String> parseParameters(@NotNull String paramString) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
String[] pairs = paramString.split("&");
|
||||
|
||||
for (String pair : pairs) {
|
||||
String[] kv = pair.split("=", 2);
|
||||
if (kv.length == 2) {
|
||||
params.put(kv[0], kv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
private @NotNull String replacePlaceholders(String template, Map<String, String> params, String serverId) {
|
||||
Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
while (matcher.find()) {
|
||||
String placeholder = matcher.group(1);
|
||||
String replacement = getReplacement(placeholder, params, serverId);
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String getReplacement(String placeholder, @NotNull Map<String, String> params, String serverId) {
|
||||
// 首先检查参数映射
|
||||
if (params.containsKey(placeholder)) {
|
||||
String value = params.get(placeholder);
|
||||
// 如果值是占位符,递归处理
|
||||
if (value != null && value.startsWith("{") && value.endsWith("}")) {
|
||||
String innerPlaceholder = value.substring(1, value.length() - 1);
|
||||
return getReplacement(innerPlaceholder, params, serverId);
|
||||
}
|
||||
return value != null ? value : "";
|
||||
}
|
||||
|
||||
// 内置占位符
|
||||
return switch (placeholder) {
|
||||
case "serverId" -> serverId != null ? serverId : "Unknown Server";
|
||||
case "player" -> {
|
||||
try {
|
||||
yield Minecraft.getInstance().getUser().getName();
|
||||
} catch (Exception e) {
|
||||
yield "Unknown Player";
|
||||
}
|
||||
}
|
||||
case "playerCount" -> String.valueOf(getOnlinePlayerCount());
|
||||
case "serverName" -> Minecraft.getInstance().getCurrentServer() != null ?
|
||||
Minecraft.getInstance().getCurrentServer().name : "Unknown Server";
|
||||
case "currentTime" -> String.valueOf(System.currentTimeMillis());
|
||||
case "ip" -> Minecraft.getInstance().getCurrentServer() != null ?
|
||||
Minecraft.getInstance().getCurrentServer().ip : "Unknown IP";
|
||||
case "version" -> Minecraft.getInstance().getVersionType();
|
||||
case "key" -> {
|
||||
try {
|
||||
// 获取打开菜单的按键名称
|
||||
yield KeyBindingHandler.OPEN_GUI_KEY.getKey().getDisplayName().getString();
|
||||
} catch (Exception e) {
|
||||
yield "Unknown Key";
|
||||
}
|
||||
}
|
||||
default -> "{" + placeholder + "}";
|
||||
};
|
||||
}
|
||||
|
||||
private String handleStringFormat(String text, Map<String, String> params, String serverId) {
|
||||
Matcher matcher = FORMAT_PATTERN.matcher(text);
|
||||
StringBuilder result = new StringBuilder();
|
||||
int index = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
String formatType = matcher.group(1);
|
||||
String replacement = getStringFormatReplacement(formatType, index, params, serverId);
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
index++;
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String getStringFormatReplacement(String formatType, int index, Map<String, String> params, String serverId) {
|
||||
// 尝试从参数中获取值
|
||||
String paramValue = null;
|
||||
|
||||
// 尝试不同的参数键名
|
||||
String[] possibleKeys = {
|
||||
"arg" + index,
|
||||
"param" + index,
|
||||
"p" + index,
|
||||
String.valueOf(index)
|
||||
};
|
||||
|
||||
for (String key : possibleKeys) {
|
||||
if (params.containsKey(key)) {
|
||||
paramValue = params.get(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到参数,使用默认值
|
||||
if (paramValue == null) {
|
||||
paramValue = getDefaultFormattedValue(index, serverId);
|
||||
}
|
||||
|
||||
// 根据格式类型格式化
|
||||
try {
|
||||
return switch (formatType) {
|
||||
case "d" -> {
|
||||
try {
|
||||
yield String.valueOf(Integer.parseInt(paramValue));
|
||||
} catch (NumberFormatException e) {
|
||||
yield "0";
|
||||
}
|
||||
}
|
||||
case "f" -> {
|
||||
try {
|
||||
yield String.format("%.1f", Double.parseDouble(paramValue));
|
||||
} catch (NumberFormatException e) {
|
||||
yield "0.0";
|
||||
}
|
||||
}
|
||||
default -> paramValue;
|
||||
};
|
||||
} catch (Exception e) {
|
||||
return paramValue;
|
||||
}
|
||||
}
|
||||
|
||||
private String getDefaultFormattedValue(int index, String serverId) {
|
||||
return switch (index) {
|
||||
case 0 -> serverId != null ? serverId : "Server";
|
||||
case 1 -> String.valueOf(getOnlinePlayerCount());
|
||||
case 2 -> {
|
||||
try {
|
||||
yield Minecraft.getInstance().getUser().getName();
|
||||
} catch (Exception e) {
|
||||
yield "Player";
|
||||
}
|
||||
}
|
||||
default -> "?";
|
||||
};
|
||||
}
|
||||
|
||||
private int getOnlinePlayerCount() {
|
||||
try {
|
||||
if (Minecraft.getInstance().getConnection() != null) {
|
||||
return Minecraft.getInstance().getConnection().getOnlinePlayers().size();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略错误
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (button == 0) {
|
||||
|
|
@ -38,6 +286,7 @@ public class ServerSelectionList extends ObjectSelectionList<ServerSelectionList
|
|||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
public static class ServerEntry extends ObjectSelectionList.Entry<ServerEntry> {
|
||||
private final Component displayName;
|
||||
private final String serverId;
|
||||
|
|
@ -89,4 +338,4 @@ public class ServerSelectionList extends ObjectSelectionList<ServerSelectionList
|
|||
return displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.leisuretimedock.crossmod.config;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
|
@ -11,23 +10,51 @@ public class CrossServerConfig {
|
|||
public static ForgeConfigSpec SPEC;
|
||||
public static final ForgeConfigSpec.ConfigValue<List<? extends String>> SERVER_LIST;
|
||||
public static final ForgeConfigSpec.BooleanValue DISABLED_JOIN_QUIT_MESSAGE;
|
||||
|
||||
static {
|
||||
BUILDER.comment("Cross Server Config").push("servers");
|
||||
SERVER_LIST = BUILDER
|
||||
.comment("Server list in format: <server_name>: <translate_key>")
|
||||
.comment(
|
||||
"Server list in format: <server_name>: <translate_key>",
|
||||
"",
|
||||
"Supported formats:",
|
||||
" 1. Translation key: lobby: ltd.mod.client.menu.button.hub",
|
||||
" 2. Translation key with parameters: survival: ltd.mod.client.menu.button.survival?player={player}&count={playerCount}",
|
||||
" 3. Direct text: custom1: H Custom Lobby",
|
||||
" 4. Direct text with color: custom2: §a[Hub] §fServer",
|
||||
"",
|
||||
"Available placeholders:",
|
||||
" {player} - Current player name",
|
||||
" {playerCount} - Online players count",
|
||||
" {serverId} - Server ID",
|
||||
" {serverName} - Current server name",
|
||||
" {currentTime} - Current timestamp",
|
||||
" {ip} - Server IP",
|
||||
" {version} - Minecraft version",
|
||||
" {key} - Hotkey name",
|
||||
"",
|
||||
"Examples:",
|
||||
" lobby: ltd.mod.client.menu.button.hub",
|
||||
" survival: ltd.mod.client.menu.button.survival?player={player}",
|
||||
" custom: H My Server",
|
||||
" test: ltd.mod.client.menu.button.test?player={player}&count={playerCount}"
|
||||
)
|
||||
.defineList("serverList",
|
||||
Arrays.asList(
|
||||
"lobby: ltd.mod.client.menu.button.hub",
|
||||
"survival: ltd.mod.client.menu.button.survival"
|
||||
"survival: ltd.mod.client.menu.button.survival",
|
||||
"skyblock: ltd.mod.client.menu.button.skyblock",
|
||||
"resource: ltd.mod.client.menu.button.resource",
|
||||
"minigame: ltd.mod.client.menu.button.minigame"
|
||||
),
|
||||
obj -> obj instanceof String str && checkSyntax(str)
|
||||
obj -> obj instanceof String str && CrossServerConfigManager.SYNTAX.matcher(str).matches()
|
||||
);
|
||||
BUILDER.pop();
|
||||
DISABLED_JOIN_QUIT_MESSAGE = BUILDER.comment("Disable join or quit message")
|
||||
.define("disabled_join_quit_message", false);
|
||||
|
||||
DISABLED_JOIN_QUIT_MESSAGE = BUILDER
|
||||
.comment("Disable join or quit message")
|
||||
.define("disabled_join_quit_message", false);
|
||||
|
||||
SPEC = BUILDER.build();
|
||||
}
|
||||
public static boolean checkSyntax(@NotNull String input) {
|
||||
return CrossServerConfigManager.SYNTAX.matcher(input).matches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,18 @@ import static java.util.regex.Pattern.compile;
|
|||
@Slf4j
|
||||
public class CrossServerConfigManager {
|
||||
public static CrossServerConfigManager INSTANCE = new CrossServerConfigManager();
|
||||
public static final Pattern SYNTAX =
|
||||
compile("([a-zA-Z]\\w+):\\s+([_.\\w]+)");
|
||||
|
||||
// 支持更灵活的格式: server_name: translate_key?param1=value1¶m2=value2
|
||||
// 或者 server_name: translate_key
|
||||
// 或者 server_name: 直接文本
|
||||
public static final Pattern SYNTAX = compile(
|
||||
"^([a-zA-Z]\\w+):\\s*(.+)$" // 匹配 server_name: 后面的任何内容
|
||||
);
|
||||
|
||||
// 用于解析参数
|
||||
private static final Pattern PARAM_PATTERN = compile("\\?(.+)$");
|
||||
private static final Pattern KEY_PATTERN = compile("^([a-zA-Z_.]+)");
|
||||
|
||||
/**
|
||||
* The constant cacheTag.
|
||||
*/
|
||||
|
|
@ -35,23 +45,128 @@ public class CrossServerConfigManager {
|
|||
@Getter
|
||||
private boolean disabledJoinQuitMessage = false;
|
||||
|
||||
/**
|
||||
* 解析服务器配置,支持带参数的翻译键
|
||||
*
|
||||
* @param servers 配置列表
|
||||
* @return 不可修改的服务器映射
|
||||
*/
|
||||
private @NotNull @Unmodifiable Map<String, String> parseServer(@NotNull List<? extends String> servers) {
|
||||
Map<String, String> serverMap = new TreeMap<>();
|
||||
for (String server : servers) {
|
||||
Matcher matcher = SYNTAX.matcher(server);
|
||||
if (matcher.matches()) {
|
||||
String key = matcher.group(1); // 第一部分:[a-zA-Z]\w+
|
||||
String value = matcher.group(2); // 第二部分:[_.\w]+
|
||||
if(!serverMap.containsKey(key)) {
|
||||
serverMap.put(key, value);
|
||||
String key = matcher.group(1).trim(); // 服务器ID
|
||||
String value = matcher.group(2).trim(); // 翻译键或直接文本
|
||||
|
||||
if (!serverMap.containsKey(key)) {
|
||||
// 验证并规范化值
|
||||
String normalizedValue = normalizeValue(value);
|
||||
serverMap.put(key, normalizedValue);
|
||||
log.debug("Loaded server: {} -> {}", key, normalizedValue);
|
||||
} else {
|
||||
log.warn("Duplicate server name '{}' found in config, skip it", key);
|
||||
}
|
||||
} else {
|
||||
log.warn("Invalid server entry format: '{}', expected: 'server_name: translate_key'", server);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(serverMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化值,确保格式正确
|
||||
* 支持:
|
||||
* 1. 纯翻译键: "ltd.mod.client.menu.button.hub"
|
||||
* 2. 带参数的翻译键: "ltd.mod.client.menu.button.hub?player={player}&count={playerCount}"
|
||||
* 3. 直接文本: "🏠 Hub Server"
|
||||
* 4. 带格式的文本: "§a[Hub] §fServer"
|
||||
*/
|
||||
private String normalizeValue(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return value;
|
||||
}
|
||||
|
||||
value = value.trim();
|
||||
|
||||
// 检查是否是翻译键格式 (包含字母、数字、下划线、点)
|
||||
Matcher keyMatcher = KEY_PATTERN.matcher(value);
|
||||
if (keyMatcher.find()) {
|
||||
String potentialKey = keyMatcher.group(1);
|
||||
// 如果匹配到的是翻译键格式,保留原样
|
||||
if (potentialKey.matches("^[a-zA-Z_][a-zA-Z0-9_.]*$")) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是翻译键格式,作为直接文本处理
|
||||
// 检查是否包含参数
|
||||
if (value.contains("?")) {
|
||||
// 可能是格式: "text?param=value"
|
||||
String[] parts = value.split("\\?", 2);
|
||||
String prefix = parts[0];
|
||||
// 如果前缀看起来像翻译键,保留;否则作为文本
|
||||
if (prefix.matches("^[a-zA-Z_][a-zA-Z0-9_.]*$")) {
|
||||
return value; // 保留完整的翻译键+参数格式
|
||||
}
|
||||
}
|
||||
|
||||
// 默认返回原值
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取带参数的翻译键
|
||||
* 如果值是翻译键格式,返回原值;否则返回null
|
||||
*/
|
||||
public String getTranslateKey(String serverId) {
|
||||
String value = servers.get(serverId);
|
||||
if (value == null) return null;
|
||||
|
||||
// 检查是否是翻译键格式(包含字母、数字、下划线、点)
|
||||
if (value.matches("^[a-zA-Z_][a-zA-Z0-9_.]*\\??.*$")) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直接文本
|
||||
* 如果值不是翻译键格式,返回原值;否则返回null
|
||||
*/
|
||||
public String getDirectText(String serverId) {
|
||||
String value = servers.get(serverId);
|
||||
if (value == null) return null;
|
||||
|
||||
// 如果不是翻译键格式,作为直接文本
|
||||
if (!value.matches("^[a-zA-Z_][a-zA-Z0-9_.]*\\??.*$")) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务器是否使用翻译键
|
||||
*/
|
||||
public boolean isTranslateKey(String serverId) {
|
||||
return getTranslateKey(serverId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器显示文本(用于搜索)
|
||||
*/
|
||||
public String getDisplayText(String serverId) {
|
||||
String value = servers.get(serverId);
|
||||
if (value == null) return serverId;
|
||||
|
||||
// 如果是翻译键,返回翻译键本身(将在ServerSelectionList中解析)
|
||||
if (value.matches("^[a-zA-Z_][a-zA-Z0-9_.]*\\??.*$")) {
|
||||
return value;
|
||||
}
|
||||
// 否则返回直接文本
|
||||
return value;
|
||||
}
|
||||
|
||||
public void reloadAll() {
|
||||
try {
|
||||
clear();
|
||||
|
|
@ -59,13 +174,20 @@ public class CrossServerConfigManager {
|
|||
disabledJoinQuitMessage = CrossServerConfig.DISABLED_JOIN_QUIT_MESSAGE.get();
|
||||
cacheHash = -1;
|
||||
cacheTag = serializeToNBT();
|
||||
log.debug("Configs reloaded");
|
||||
log.info("Configs reloaded, loaded {} servers", servers.size());
|
||||
|
||||
// 打印加载的服务器列表(用于调试)
|
||||
if (!servers.isEmpty()) {
|
||||
log.debug("Loaded servers:");
|
||||
servers.forEach((id, value) ->
|
||||
log.debug(" {} -> {}", id, value)
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to reload configs", e);
|
||||
cacheHash = -1;
|
||||
cacheTag = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,6 +206,7 @@ public class CrossServerConfigManager {
|
|||
}
|
||||
CompoundTag tag = new CompoundTag();
|
||||
serializeMap(tag, "servers", this.servers);
|
||||
tag.putBoolean("disabledJoinQuitMessage", disabledJoinQuitMessage);
|
||||
cacheHash = calculateConfigHash();
|
||||
cacheTag = tag;
|
||||
return tag;
|
||||
|
|
@ -98,7 +221,6 @@ public class CrossServerConfigManager {
|
|||
parent.put(key, mapTag);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从NBT反序列化配置管理器状态
|
||||
*
|
||||
|
|
@ -109,6 +231,9 @@ public class CrossServerConfigManager {
|
|||
cacheTag = null;
|
||||
clear();
|
||||
deserializeMap(tag, "servers", servers);
|
||||
if (tag.contains("disabledJoinQuitMessage")) {
|
||||
disabledJoinQuitMessage = tag.getBoolean("disabledJoinQuitMessage");
|
||||
}
|
||||
cacheTag = serializeToNBT();
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +281,8 @@ public class CrossServerConfigManager {
|
|||
int hash = 0x811c9dc5; // FNV偏移基础值
|
||||
TreeMap<String, String> sortedMap = new TreeMap<>(servers);
|
||||
hash = fnv1aHashMap(hash, sortedMap);
|
||||
// 包含 disabledJoinQuitMessage 在哈希中
|
||||
hash = fnv1aHashString(hash, String.valueOf(disabledJoinQuitMessage));
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
|
@ -166,11 +293,11 @@ public class CrossServerConfigManager {
|
|||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
private int fnv1aHashMap(int hash, @NotNull Map<String, String> map) {
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
hash = fnv1aHashString(hash, entry.getKey());
|
||||
hash = fnv1aHashString(hash, entry.getValue());
|
||||
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
|
@ -183,4 +310,25 @@ public class CrossServerConfigManager {
|
|||
NetworkHandler.sendToAllPlayer(new CommonConfigHashInformPacket(cacheHash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器数量
|
||||
*/
|
||||
public int getServerCount() {
|
||||
return servers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务器是否存在于配置中
|
||||
*/
|
||||
public boolean hasServer(String serverId) {
|
||||
return servers.containsKey(serverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器配置值
|
||||
*/
|
||||
public String getServerValue(String serverId) {
|
||||
return servers.get(serverId);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import org.slf4j.Logger;
|
|||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
|
@ -30,7 +31,7 @@ public class MixinPlayerList {
|
|||
cancellable = true
|
||||
)
|
||||
private void onBroadcastSystemMessage(Component message, boolean bypassHiddenChat, CallbackInfo ci) {
|
||||
if (shouldCancel(message)) {
|
||||
if (crossServerTeleport$shouldCancel(message)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
|
@ -44,12 +45,13 @@ public class MixinPlayerList {
|
|||
cancellable = true
|
||||
)
|
||||
private void onBroadcastSystemMessage(Component serverMessage, Function<ServerPlayer, Component> playerMessageFactory, boolean bypassHiddenChat, CallbackInfo ci) {
|
||||
if (shouldCancel(serverMessage)) {
|
||||
if (crossServerTeleport$shouldCancel(serverMessage)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldCancel(Component message) {
|
||||
@Unique
|
||||
private boolean crossServerTeleport$shouldCancel(Component message) {
|
||||
if (!CrossServerConfigManager.INSTANCE.isDisabledJoinQuitMessage()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
"ltd.mod.client.menu.button.skyblock": "S SkyBlock",
|
||||
"ltd.mod.client.menu.button.resource": "R Resource",
|
||||
"ltd.mod.client.menu.button.minigame": "M MiniGame",
|
||||
"ltd.mod.client.menu.button.custom": "{icon} ${name}",
|
||||
"ltd.mod.client.menu.search": "Search servers...",
|
||||
"ltd.mod.client.menu.checkbox.show_trans_tip": "Show cross server tip overlay",
|
||||
"ltd.mod.client.menu.checkbox.show_ping_stat": "Show ping stat overlay",
|
||||
"ltd.mod.client.negotiating": "Negotiating...",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"ltd.mod.client.menu.button.skyblock": "S 空岛服",
|
||||
"ltd.mod.client.menu.button.resource": "R 资源服",
|
||||
"ltd.mod.client.menu.button.minigame": "M 小游戏服",
|
||||
"ltd.mod.client.menu.search": "搜索服务器..",
|
||||
"ltd.mod.client.menu.checkbox.show_trans_tip": "显示传送提示",
|
||||
"ltd.mod.client.menu.checkbox.show_ping_stat": "显示Ping状态",
|
||||
"ltd.mod.client.negotiating": "重定向中 ...",
|
||||
|
|
@ -56,5 +57,9 @@
|
|||
"crossmod.command.debug.enabled": "已开启",
|
||||
"crossmod.command.debug.disabled": "已关闭",
|
||||
"crossmod.command.debug.enabled_short": "§a已开启",
|
||||
"crossmod.command.debug.disabled_short": "§c已关闭"
|
||||
"crossmod.command.debug.disabled_short": "§c已关闭",
|
||||
"ltd.mod.client.menu.button.1": "H 大厅",
|
||||
"ltd.mod.client.menu.button.2": "S 生存",
|
||||
"ltd.mod.request.goto": "请求跳转 %s",
|
||||
"ltd.mod.client.menu.button.custom": "{icon} ${name}"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user