This commit is contained in:
Xujiayao 2026-03-16 20:36:33 +08:00 committed by Jason Xu
parent 6a9036dbbc
commit 758d1634d2
12 changed files with 83 additions and 61 deletions

View file

@ -142,7 +142,8 @@ DMCC 在 Client 端提供独立的 `whitelist` 代理命令(默认所需权限
当 DMCC 处于 `standalone + multi_server_client` 架构时,不同子服务器可使用不同 OP 映射策略。 当 DMCC 处于 `standalone + multi_server_client` 架构时,不同子服务器可使用不同 OP 映射策略。
`standalone` 配置的 `user_mappings``role_mappings` 中,每个条目包含一个顶层 `op_level`(给 Standalone 自身查询使用),以及一个 `standalone` 配置的 `user_mappings``role_mappings` 中,每个条目包含一个顶层 `op_level`(给 Standalone 自身查询使用),以及一个
`server_overrides` 列表字典。若 `server_overrides` 中没有某子服务器的对应条目,则该子服务器自动降级使用顶层 `op_level` 作为默认回退值。 `server_overrides` 列表字典。若 `server_overrides` 中没有某子服务器的对应条目,则该子服务器自动降级使用顶层 `op_level`
作为默认回退值。
## 7. 命令列表与权限参考 ## 7. 命令列表与权限参考

View file

@ -16,10 +16,14 @@ import java.util.List;
*/ */
public class DiscordEventPacket extends Packet { public class DiscordEventPacket extends Packet {
/** The type of Discord event. */ /**
* The type of Discord event.
*/
public EventType type; public EventType type;
/** Pre-built rich text segments for the main message line. */ /**
* Pre-built rich text segments for the main message line.
*/
public List<TextSegment> segments; public List<TextSegment> segments;
/** /**
@ -67,10 +71,14 @@ public class DiscordEventPacket extends Packet {
* @author Xujiayao * @author Xujiayao
*/ */
public enum EventType { public enum EventType {
/** A regular Discord chat message forwarded to Minecraft. */ /**
* A regular Discord chat message forwarded to Minecraft.
*/
CHAT, CHAT,
/** A Discord command execution notification forwarded to Minecraft. */ /**
* A Discord command execution notification forwarded to Minecraft.
*/
COMMAND COMMAND
} }
} }

View file

@ -21,22 +21,34 @@ public class TextSegment implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** The display text of this segment. */ /**
* The display text of this segment.
*/
public String text; public String text;
/** Whether the text should be rendered in bold. */ /**
* Whether the text should be rendered in bold.
*/
public boolean bold; public boolean bold;
/** Whether the text should be rendered in italic. */ /**
* Whether the text should be rendered in italic.
*/
public boolean italic; public boolean italic;
/** Whether the text should be rendered with underline. */ /**
* Whether the text should be rendered with underline.
*/
public boolean underlined; public boolean underlined;
/** Whether the text should be rendered with strikethrough. */ /**
* Whether the text should be rendered with strikethrough.
*/
public boolean strikethrough; public boolean strikethrough;
/** Whether the text should be rendered obfuscated. */ /**
* Whether the text should be rendered obfuscated.
*/
public boolean obfuscated; public boolean obfuscated;
/** /**

View file

@ -21,8 +21,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**

View file

@ -11,7 +11,9 @@ import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.Webhook; import net.dv8tion.jda.api.entities.Webhook;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
@ -295,7 +297,7 @@ public class DiscordManager {
* @param discordId The Discord user ID. * @param discordId The Discord user ID.
* @return The User object, or null if JDA is not available or user not found. * @return The User object, or null if JDA is not available or user not found.
*/ */
public static net.dv8tion.jda.api.entities.User retrieveUser(String discordId) { public static User retrieveUser(String discordId) {
if (jda == null) return null; if (jda == null) return null;
try { try {
return jda.retrieveUserById(discordId).complete(); return jda.retrieveUserById(discordId).complete();
@ -310,11 +312,11 @@ public class DiscordManager {
* @param discordId The Discord user ID. * @param discordId The Discord user ID.
* @return The Member object, or null if JDA is not available or member not found. * @return The Member object, or null if JDA is not available or member not found.
*/ */
public static net.dv8tion.jda.api.entities.Member retrieveMember(String discordId) { public static Member retrieveMember(String discordId) {
if (jda == null) return null; if (jda == null) return null;
for (var guild : jda.getGuilds()) { for (var guild : jda.getGuilds()) {
try { try {
net.dv8tion.jda.api.entities.Member member = guild.retrieveMemberById(discordId).complete(); Member member = guild.retrieveMemberById(discordId).complete();
if (member != null) { if (member != null) {
return member; return member;
} }

View file

@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.xujiayao.discord_mc_chat.network.packets.events.TextSegment; import com.xujiayao.discord_mc_chat.network.packets.events.TextSegment;
import com.xujiayao.discord_mc_chat.server.linking.LinkedAccountManager; import com.xujiayao.discord_mc_chat.server.linking.LinkedAccountManager;
import com.xujiayao.discord_mc_chat.utils.config.ConfigManager; import com.xujiayao.discord_mc_chat.utils.config.ConfigManager;
import com.xujiayao.discord_mc_chat.utils.config.ModeManager;
import com.xujiayao.discord_mc_chat.utils.i18n.I18nManager; import com.xujiayao.discord_mc_chat.utils.i18n.I18nManager;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
@ -370,13 +369,13 @@ public class DiscordMessageParser {
* Parses the raw text content of a Discord message, handling mentions, emojis, * Parses the raw text content of a Discord message, handling mentions, emojis,
* markdown, and hyperlinks inline. * markdown, and hyperlinks inline.
* *
* @param raw The raw content string. * @param raw The raw content string.
* @param message The Discord message (for resolving mentions). * @param message The Discord message (for resolving mentions).
* @param parseMentions Whether to parse mentions. * @param parseMentions Whether to parse mentions.
* @param parseCustomEmojis Whether to parse custom emojis. * @param parseCustomEmojis Whether to parse custom emojis.
* @param parseUnicodeEmojis Whether to parse unicode emojis. * @param parseUnicodeEmojis Whether to parse unicode emojis.
* @param parseMarkdown Whether to parse markdown formatting. * @param parseMarkdown Whether to parse markdown formatting.
* @param parseHyperlinks Whether to parse hyperlinks. * @param parseHyperlinks Whether to parse hyperlinks.
* @return The list of text segments for the raw content. * @return The list of text segments for the raw content.
*/ */
private static List<TextSegment> parseRawContent(String raw, Message message, private static List<TextSegment> parseRawContent(String raw, Message message,
@ -721,32 +720,6 @@ public class DiscordMessageParser {
return "blue"; return "blue";
} }
/**
* Internal record representing a span of raw text that maps to a special token
* (mention, emoji, link, etc.).
*/
private record TokenSpan(int start, int end, TextSegment segment) {
}
/**
* Internal record representing a markdown-formatted span of text.
*/
private record MarkdownSpan(int start, int end, String innerText, MarkdownType type) {
}
/**
* Types of markdown formatting.
*/
private enum MarkdownType {
BOLD,
ITALIC,
UNDERLINE,
STRIKETHROUGH,
SPOILER,
CODE_BLOCK,
INLINE_CODE
}
/** /**
* Safely truncates a string to the given maximum character count without splitting * Safely truncates a string to the given maximum character count without splitting
* surrogate pairs (multi-byte emoji characters). * surrogate pairs (multi-byte emoji characters).
@ -765,4 +738,30 @@ public class DiscordMessageParser {
} }
return text.substring(0, maxLen); return text.substring(0, maxLen);
} }
/**
* Types of markdown formatting.
*/
private enum MarkdownType {
BOLD,
ITALIC,
UNDERLINE,
STRIKETHROUGH,
SPOILER,
CODE_BLOCK,
INLINE_CODE
}
/**
* Internal record representing a span of raw text that maps to a special token
* (mention, emoji, link, etc.).
*/
private record TokenSpan(int start, int end, TextSegment segment) {
}
/**
* Internal record representing a markdown-formatted span of text.
*/
private record MarkdownSpan(int start, int end, String innerText, MarkdownType type) {
}
} }

View file

@ -142,11 +142,11 @@ public class CoreEvents {
* The handler should convert the pre-built {@link TextSegment} lists into * The handler should convert the pre-built {@link TextSegment} lists into
* Minecraft Components and broadcast them to all online players. * Minecraft Components and broadcast them to all online players.
* *
* @param segments The main message line segments. * @param segments The main message line segments.
* @param replySegments The reply context line segments (may be null or empty). * @param replySegments The reply context line segments (may be null or empty).
* @param mentionNotificationText The mention notification text (may be null). * @param mentionNotificationText The mention notification text (may be null).
* @param mentionNotificationStyle The notification style: "action_bar", "title", or "chat". * @param mentionNotificationStyle The notification style: "action_bar", "title", or "chat".
* @param mentionedPlayerUuids UUIDs of players who should receive mention notifications. * @param mentionedPlayerUuids UUIDs of players who should receive mention notifications.
*/ */
public record DiscordChatMessageEvent( public record DiscordChatMessageEvent(
List<TextSegment> segments, List<TextSegment> segments,

View file

@ -153,7 +153,7 @@ account_linking:
user_mappings: user_mappings:
- user: "xujiayao" - user: "xujiayao"
op_level: 4 op_level: 4
server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。 server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。
- server: "SMP" - server: "SMP"
op_level: 4 op_level: 4
- server: "CMP" - server: "CMP"
@ -163,7 +163,7 @@ account_linking:
# 示例 1拥有 "Admins" 角色的用户将被视为 OP 3。 # 示例 1拥有 "Admins" 角色的用户将被视为 OP 3。
- role: "Admins" - role: "Admins"
op_level: 3 op_level: 3
server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。 server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。
- server: "SMP" - server: "SMP"
op_level: 3 op_level: 3
- server: "CMP" - server: "CMP"
@ -171,7 +171,7 @@ account_linking:
# 示例 2可将基础认证角色映射为 OP 0使其有权执行无需特殊权限的 DMCC 命令(如委托执行白名单)。 # 示例 2可将基础认证角色映射为 OP 0使其有权执行无需特殊权限的 DMCC 命令(如委托执行白名单)。
- role: "Players" - role: "Players"
op_level: 0 op_level: 0
server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。 server_overrides: # 如果该子服务器不需要覆盖上方定义的 OP 等级设置,可以删除该项。此列表可以为空。
- server: "SMP" - server: "SMP"
op_level: 0 op_level: 0
- server: "CMP" - server: "CMP"

View file

@ -49,7 +49,7 @@ common:
discord_to_minecraft: discord_to_minecraft:
mentioned: "{effective_name} mentioned you!" mentioned: "{effective_name} mentioned you!"
response: # 回复其它用户的消息时,原消息的格式 response: # 回复其它用户的消息时,原消息的格式
- text: " ┌──── " - text: " ┌──── "
bold: true bold: true
color: "dark_gray" color: "dark_gray"

View file

@ -49,7 +49,7 @@ common:
discord_to_minecraft: discord_to_minecraft:
mentioned: "{effective_name} 提及了你!" mentioned: "{effective_name} 提及了你!"
response: # 回复其它用户的消息时,原消息的格式 response: # 回复其它用户的消息时,原消息的格式
- text: " ┌──── " - text: " ┌──── "
bold: true bold: true
color: "dark_gray" color: "dark_gray"

View file

@ -71,6 +71,7 @@ import java.util.concurrent.TimeUnit;
*/ */
public class MinecraftEventHandler { public class MinecraftEventHandler {
private static final String DEFAULT_MENTION_STYLE = "title";
private static MinecraftServer serverInstance; private static MinecraftServer serverInstance;
/** /**
@ -657,7 +658,7 @@ public class MinecraftEventHandler {
* Builds a clickable Component that suggests a command (fills the chat input) when clicked. * Builds a clickable Component that suggests a command (fills the chat input) when clicked.
* Displayed in green with brackets. * Displayed in green with brackets.
* *
* @param command The command to suggest (fill into chat input). * @param command The command to suggest (fill into chat input).
* @return A clickable Component. * @return A clickable Component.
*/ */
private static Component buildSuggestCommand(String command) { private static Component buildSuggestCommand(String command) {
@ -805,8 +806,6 @@ public class MinecraftEventHandler {
} }
} }
private static final String DEFAULT_MENTION_STYLE = "title";
/** /**
* Sends a mention notification to a player using the configured style. * Sends a mention notification to a player using the configured style.
* *

View file

@ -7,6 +7,7 @@ import com.xujiayao.discord_mc_chat.utils.StringUtils;
import com.xujiayao.discord_mc_chat.utils.i18n.I18nManager; import com.xujiayao.discord_mc_chat.utils.i18n.I18nManager;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackResources;
import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.PackType;
@ -245,7 +246,7 @@ public class TranslationManager {
packResources1.getNamespaces(PackType.CLIENT_RESOURCES).forEach(namespace -> { packResources1.getNamespaces(PackType.CLIENT_RESOURCES).forEach(namespace -> {
IoSupplier<InputStream> supplier = packResources1.getResource( IoSupplier<InputStream> supplier = packResources1.getResource(
PackType.CLIENT_RESOURCES, PackType.CLIENT_RESOURCES,
net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(namespace, "lang/" + language + ".json") ResourceLocation.fromNamespaceAndPath(namespace, "lang/" + language + ".json")
); );
if (supplier != null) { if (supplier != null) {