mirror of
				https://github.com/SikongJueluo/cc-utils.git
				synced 2025-11-04 11:17:50 +08:00 
			
		
		
		
	fix: wrong type, chat manager unicode string; feature: accesscontrol welcome message and chinese support
This commit is contained in:
		@@ -14,7 +14,9 @@ export interface AppContext {
 | 
			
		||||
  configFilepath: string;
 | 
			
		||||
  reloadConfig: () => void;
 | 
			
		||||
  logger: CCLog;
 | 
			
		||||
  print: (message: string) => void;
 | 
			
		||||
  print: (
 | 
			
		||||
    message: string | MinecraftTextComponent | MinecraftTextComponent[],
 | 
			
		||||
  ) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getGroupNames(config: AccessConfig) {
 | 
			
		||||
@@ -25,14 +27,14 @@ function getGroupNames(config: AccessConfig) {
 | 
			
		||||
 | 
			
		||||
const addCommand: Command<AppContext> = {
 | 
			
		||||
  name: "add",
 | 
			
		||||
  description: "Add player to group",
 | 
			
		||||
  description: "添加玩家到用户组",
 | 
			
		||||
  args: [
 | 
			
		||||
    {
 | 
			
		||||
      name: "userGroup",
 | 
			
		||||
      description: "Group to add player to",
 | 
			
		||||
      description: "要添加到的用户组",
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    { name: "playerName", description: "Player to add", required: true },
 | 
			
		||||
    { name: "playerName", description: "要添加的玩家", required: true },
 | 
			
		||||
  ],
 | 
			
		||||
  action: ({ args, context }) => {
 | 
			
		||||
    const [groupName, playerName] = [
 | 
			
		||||
@@ -49,9 +51,11 @@ const addCommand: Command<AppContext> = {
 | 
			
		||||
      const group = config.usersGroups.find((g) => g.groupName === groupName);
 | 
			
		||||
      if (!group) {
 | 
			
		||||
        const groupNames = getGroupNames(config);
 | 
			
		||||
        context.print(
 | 
			
		||||
          `Invalid group: ${groupName}. Available groups: ${groupNames.join(", ")}`,
 | 
			
		||||
        );
 | 
			
		||||
        context.print({
 | 
			
		||||
          text: `无效的用户组: ${groupName}. 可用用户组: ${groupNames.join(
 | 
			
		||||
            ", ",
 | 
			
		||||
          )}`,
 | 
			
		||||
        });
 | 
			
		||||
        return Ok.EMPTY;
 | 
			
		||||
      }
 | 
			
		||||
      group.groupUsers ??= [];
 | 
			
		||||
@@ -62,21 +66,21 @@ const addCommand: Command<AppContext> = {
 | 
			
		||||
 | 
			
		||||
    saveConfig(config, context.configFilepath);
 | 
			
		||||
    context.reloadConfig();
 | 
			
		||||
    context.print(`Added player ${playerName} to ${groupName}`);
 | 
			
		||||
    context.print({ text: `已添加玩家 ${playerName} 到 ${groupName}` });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const delCommand: Command<AppContext> = {
 | 
			
		||||
  name: "del",
 | 
			
		||||
  description: "Delete player from group",
 | 
			
		||||
  description: "从用户组删除玩家",
 | 
			
		||||
  args: [
 | 
			
		||||
    {
 | 
			
		||||
      name: "userGroup",
 | 
			
		||||
      description: "Group to delete player from",
 | 
			
		||||
      description: "要从中删除玩家的用户组",
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    { name: "playerName", description: "Player to delete", required: true },
 | 
			
		||||
    { name: "playerName", description: "要删除的玩家", required: true },
 | 
			
		||||
  ],
 | 
			
		||||
  action: ({ args, context }) => {
 | 
			
		||||
    const [groupName, playerName] = [
 | 
			
		||||
@@ -85,7 +89,7 @@ const delCommand: Command<AppContext> = {
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    if (groupName === "admin") {
 | 
			
		||||
      context.print("Could not delete admin, please edit config file.");
 | 
			
		||||
      context.print({ text: "无法删除管理员, 请直接编辑配置文件。" });
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -94,9 +98,11 @@ const delCommand: Command<AppContext> = {
 | 
			
		||||
 | 
			
		||||
    if (!group) {
 | 
			
		||||
      const groupNames = getGroupNames(config);
 | 
			
		||||
      context.print(
 | 
			
		||||
        `Invalid group: ${groupName}. Available groups: ${groupNames.join(", ")}`,
 | 
			
		||||
      );
 | 
			
		||||
      context.print({
 | 
			
		||||
        text: `无效的用户组: ${groupName}. 可用用户组: ${groupNames.join(
 | 
			
		||||
          ", ",
 | 
			
		||||
        )}`,
 | 
			
		||||
      });
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -106,43 +112,45 @@ const delCommand: Command<AppContext> = {
 | 
			
		||||
 | 
			
		||||
    saveConfig(config, context.configFilepath);
 | 
			
		||||
    context.reloadConfig();
 | 
			
		||||
    context.print(`Deleted player ${playerName} from ${groupName}`);
 | 
			
		||||
    context.print({ text: `已从 ${groupName} 中删除玩家 ${playerName}` });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const listCommand: Command<AppContext> = {
 | 
			
		||||
  name: "list",
 | 
			
		||||
  description: "List all players with their groups",
 | 
			
		||||
  description: "列出所有玩家及其所在的用户组",
 | 
			
		||||
  action: ({ context }) => {
 | 
			
		||||
    const config = loadConfig(context.configFilepath)!;
 | 
			
		||||
    let message = `Admins : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]\n`;
 | 
			
		||||
    let message = `管理员 : [ ${config.adminGroupConfig.groupUsers.join(
 | 
			
		||||
      ", ",
 | 
			
		||||
    )} ]\n`;
 | 
			
		||||
    for (const groupConfig of config.usersGroups) {
 | 
			
		||||
      const users = groupConfig.groupUsers ?? [];
 | 
			
		||||
      message += `${groupConfig.groupName} : [ ${users.join(", ")} ]\n`;
 | 
			
		||||
    }
 | 
			
		||||
    context.print(message.trim());
 | 
			
		||||
    context.print({ text: message.trim() });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const setCommand: Command<AppContext> = {
 | 
			
		||||
  name: "set",
 | 
			
		||||
  description: "Config access control settings",
 | 
			
		||||
  description: "配置访问控制设置",
 | 
			
		||||
  args: [
 | 
			
		||||
    {
 | 
			
		||||
      name: "option",
 | 
			
		||||
      description: "Option to set (warnInterval, detectInterval, detectRange)",
 | 
			
		||||
      description: "要设置的选项 (warnInterval, detectInterval, detectRange)",
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    { name: "value", description: "Value to set", required: true },
 | 
			
		||||
    { name: "value", description: "要设置的值", required: true },
 | 
			
		||||
  ],
 | 
			
		||||
  action: ({ args, context }) => {
 | 
			
		||||
    const [option, valueStr] = [args.option as string, args.value as string];
 | 
			
		||||
    const value = parseInt(valueStr);
 | 
			
		||||
 | 
			
		||||
    if (isNaN(value)) {
 | 
			
		||||
      context.print(`Invalid value: ${valueStr}. Must be a number.`);
 | 
			
		||||
      context.print({ text: `无效的值: ${valueStr}. 必须是一个数字。` });
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -152,45 +160,45 @@ const setCommand: Command<AppContext> = {
 | 
			
		||||
    switch (option) {
 | 
			
		||||
      case "warnInterval":
 | 
			
		||||
        config.watchInterval = value;
 | 
			
		||||
        message = `Set warn interval to ${value}`;
 | 
			
		||||
        message = `已设置警告间隔为 ${value}`;
 | 
			
		||||
        break;
 | 
			
		||||
      case "detectInterval":
 | 
			
		||||
        config.detectInterval = value;
 | 
			
		||||
        message = `Set detect interval to ${value}`;
 | 
			
		||||
        message = `已设置检测间隔为 ${value}`;
 | 
			
		||||
        break;
 | 
			
		||||
      case "detectRange":
 | 
			
		||||
        config.detectRange = value;
 | 
			
		||||
        message = `Set detect range to ${value}`;
 | 
			
		||||
        message = `已设置检测范围为 ${value}`;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        context.print(
 | 
			
		||||
          `Unknown option: ${option}. Available: warnInterval, detectInterval, detectRange`,
 | 
			
		||||
        );
 | 
			
		||||
        context.print({
 | 
			
		||||
          text: `未知选项: ${option}. 可用选项: warnInterval, detectInterval, detectRange`,
 | 
			
		||||
        });
 | 
			
		||||
        return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    saveConfig(config, context.configFilepath);
 | 
			
		||||
    context.reloadConfig();
 | 
			
		||||
    context.print(message);
 | 
			
		||||
    context.print({ text: message });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const editGroupCommand: Command<AppContext> = {
 | 
			
		||||
  name: "group",
 | 
			
		||||
  description: "Edit group properties",
 | 
			
		||||
  description: "编辑用户组属性",
 | 
			
		||||
  args: [
 | 
			
		||||
    {
 | 
			
		||||
      name: "groupName",
 | 
			
		||||
      description: "Name of the group to edit",
 | 
			
		||||
      description: "要编辑的用户组名称",
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: "property",
 | 
			
		||||
      description: "Property to change (isAllowed, isNotice)",
 | 
			
		||||
      description: "要更改的属性 (isAllowed, isNotice)",
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    { name: "value", description: "New value (true/false)", required: true },
 | 
			
		||||
    { name: "value", description: "新值 (true/false)", required: true },
 | 
			
		||||
  ],
 | 
			
		||||
  action: ({ args, context }) => {
 | 
			
		||||
    const [groupName, property, valueStr] = [
 | 
			
		||||
@@ -208,15 +216,15 @@ const editGroupCommand: Command<AppContext> = {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!groupConfig) {
 | 
			
		||||
      context.print(`Group ${groupName} not found`);
 | 
			
		||||
      context.print({ text: `用户组 ${groupName} 未找到` });
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const boolValue = parseBoolean(valueStr);
 | 
			
		||||
    if (boolValue === undefined) {
 | 
			
		||||
      context.print(
 | 
			
		||||
        `Invalid boolean value: ${valueStr}. Use 'true' or 'false'.`,
 | 
			
		||||
      );
 | 
			
		||||
      context.print({
 | 
			
		||||
        text: `无效的布尔值: ${valueStr}. 请使用 'true' 或 'false'.`,
 | 
			
		||||
      });
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -224,63 +232,65 @@ const editGroupCommand: Command<AppContext> = {
 | 
			
		||||
    switch (property) {
 | 
			
		||||
      case "isAllowed":
 | 
			
		||||
        groupConfig.isAllowed = boolValue;
 | 
			
		||||
        message = `Set ${groupName}.isAllowed to ${boolValue}`;
 | 
			
		||||
        message = `已设置 ${groupName}.isAllowed 为 ${boolValue}`;
 | 
			
		||||
        break;
 | 
			
		||||
      case "isNotice":
 | 
			
		||||
        groupConfig.isNotice = boolValue;
 | 
			
		||||
        message = `Set ${groupName}.isNotice to ${boolValue}`;
 | 
			
		||||
        message = `已设置 ${groupName}.isNotice 为 ${boolValue}`;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        context.print(
 | 
			
		||||
          `Unknown property: ${property}. Available: isAllowed, isNotice`,
 | 
			
		||||
        );
 | 
			
		||||
        context.print({
 | 
			
		||||
          text: `未知属性: ${property}. 可用属性: isAllowed, isNotice`,
 | 
			
		||||
        });
 | 
			
		||||
        return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    saveConfig(config, context.configFilepath);
 | 
			
		||||
    context.reloadConfig();
 | 
			
		||||
    context.print(message);
 | 
			
		||||
    context.print({ text: message });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const editCommand: Command<AppContext> = {
 | 
			
		||||
  name: "edit",
 | 
			
		||||
  description: "Edit various configurations",
 | 
			
		||||
  description: "编辑各项配置",
 | 
			
		||||
  subcommands: new Map([["group", editGroupCommand]]),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showConfigCommand: Command<AppContext> = {
 | 
			
		||||
  name: "showconfig",
 | 
			
		||||
  description: "Show configuration",
 | 
			
		||||
  description: "显示配置",
 | 
			
		||||
  options: new Map([
 | 
			
		||||
    [
 | 
			
		||||
      "type",
 | 
			
		||||
      {
 | 
			
		||||
        name: "type",
 | 
			
		||||
        description: "Type of config to show (groups, toast, all)",
 | 
			
		||||
        description: "要显示的配置类型 (groups, toast, all)",
 | 
			
		||||
        required: false,
 | 
			
		||||
        defaultValue: "all",
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  ]),
 | 
			
		||||
  action: ({ args, context }) => {
 | 
			
		||||
    const type = args.type as string;
 | 
			
		||||
  action: ({ options, context }) => {
 | 
			
		||||
    const type = options.type as string;
 | 
			
		||||
    const config = loadConfig(context.configFilepath)!;
 | 
			
		||||
    let message = "";
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case "groups": {
 | 
			
		||||
        let groupsMessage = `Admin Group: ${config.adminGroupConfig.groupName}\n`;
 | 
			
		||||
        groupsMessage += `  Users: [${config.adminGroupConfig.groupUsers.join(", ")}]\n`;
 | 
			
		||||
        groupsMessage += `  Allowed: ${config.adminGroupConfig.isAllowed}\n`;
 | 
			
		||||
        groupsMessage += `  notice: ${config.adminGroupConfig.isNotice}\n\n`;
 | 
			
		||||
        let groupsMessage = `管理员组: ${config.adminGroupConfig.groupName}\n`;
 | 
			
		||||
        groupsMessage += `  用户: [${config.adminGroupConfig.groupUsers.join(
 | 
			
		||||
          ", ",
 | 
			
		||||
        )}]\n`;
 | 
			
		||||
        groupsMessage += `  允许: ${config.adminGroupConfig.isAllowed}\n`;
 | 
			
		||||
        groupsMessage += `  通知: ${config.adminGroupConfig.isNotice}\n\n`;
 | 
			
		||||
 | 
			
		||||
        for (const group of config.usersGroups) {
 | 
			
		||||
          groupsMessage += `Group: ${group.groupName}\n`;
 | 
			
		||||
          groupsMessage += `  Users: [${(group.groupUsers ?? []).join(", ")}]\n`;
 | 
			
		||||
          groupsMessage += `  Allowed: ${group.isAllowed}\n`;
 | 
			
		||||
          groupsMessage += `  Notice: ${group.isNotice}\n`;
 | 
			
		||||
          groupsMessage += `用户组: ${group.groupName}\n`;
 | 
			
		||||
          groupsMessage += `  用户: [${(group.groupUsers ?? []).join(", ")}]\n`;
 | 
			
		||||
          groupsMessage += `  允许: ${group.isAllowed}\n`;
 | 
			
		||||
          groupsMessage += `  通知: ${group.isNotice}\n`;
 | 
			
		||||
          groupsMessage += "\n";
 | 
			
		||||
        }
 | 
			
		||||
        message = groupsMessage.trim();
 | 
			
		||||
@@ -288,38 +298,48 @@ const showConfigCommand: Command<AppContext> = {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case "toast": {
 | 
			
		||||
        let toastMessage = "Default Toast Config:\n";
 | 
			
		||||
        toastMessage += `  Title: ${config.welcomeToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  Message: ${config.welcomeToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  Prefix: ${config.welcomeToastConfig.prefix ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Brackets: ${config.welcomeToastConfig.brackets ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Bracket Color: ${config.welcomeToastConfig.bracketColor ?? "none"}\n\n`;
 | 
			
		||||
        let toastMessage = "默认 Toast 配置:\n";
 | 
			
		||||
        toastMessage += `  标题: ${config.welcomeToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  消息: ${config.welcomeToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  前缀: ${
 | 
			
		||||
          config.welcomeToastConfig.prefix ?? "none"
 | 
			
		||||
        }\n`;
 | 
			
		||||
        toastMessage += `  括号: ${
 | 
			
		||||
          config.welcomeToastConfig.brackets ?? "none"
 | 
			
		||||
        }\n`;
 | 
			
		||||
        toastMessage += `  括号颜色: ${
 | 
			
		||||
          config.welcomeToastConfig.bracketColor ?? "none"
 | 
			
		||||
        }\n\n`;
 | 
			
		||||
 | 
			
		||||
        toastMessage += "Warn Toast Config:\n";
 | 
			
		||||
        toastMessage += `  Title: ${config.warnToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  Message: ${config.warnToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  Prefix: ${config.warnToastConfig.prefix ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Brackets: ${config.warnToastConfig.brackets ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Bracket Color: ${config.warnToastConfig.bracketColor ?? "none"}`;
 | 
			
		||||
        toastMessage += "警告 Toast 配置:\n";
 | 
			
		||||
        toastMessage += `  标题: ${config.warnToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  消息: ${config.warnToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  前缀: ${config.warnToastConfig.prefix ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  括号: ${
 | 
			
		||||
          config.warnToastConfig.brackets ?? "none"
 | 
			
		||||
        }\n`;
 | 
			
		||||
        toastMessage += `  括号颜色: ${
 | 
			
		||||
          config.warnToastConfig.bracketColor ?? "none"
 | 
			
		||||
        }`;
 | 
			
		||||
        message = toastMessage;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case "all": {
 | 
			
		||||
        let allMessage = `Detect Range: ${config.detectRange}\n`;
 | 
			
		||||
        allMessage += `Detect Interval: ${config.detectInterval}\n`;
 | 
			
		||||
        allMessage += `Warn Interval: ${config.watchInterval}\n\n`;
 | 
			
		||||
        let allMessage = `检测范围: ${config.detectRange}\n`;
 | 
			
		||||
        allMessage += `检测间隔: ${config.detectInterval}\n`;
 | 
			
		||||
        allMessage += `警告间隔: ${config.watchInterval}\n\n`;
 | 
			
		||||
        allMessage +=
 | 
			
		||||
          "Use 'showconfig groups' or 'showconfig toast' for detailed view";
 | 
			
		||||
          "使用 'showconfig --type groups' 或 'showconfig --type toast' 查看详细信息";
 | 
			
		||||
        message = allMessage;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        message = `Invalid type: ${type}. Available: groups, toast, all`;
 | 
			
		||||
        message = `无效类型: ${type}. 可用类型: groups, toast, all`;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    context.print(message);
 | 
			
		||||
    context.print({ text: message });
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -327,7 +347,7 @@ const showConfigCommand: Command<AppContext> = {
 | 
			
		||||
// Root command
 | 
			
		||||
const rootCommand: Command<AppContext> = {
 | 
			
		||||
  name: "@AC",
 | 
			
		||||
  description: "Access Control command line interface",
 | 
			
		||||
  description: "访问控制命令行界面",
 | 
			
		||||
  subcommands: new Map([
 | 
			
		||||
    ["add", addCommand],
 | 
			
		||||
    ["del", delCommand],
 | 
			
		||||
@@ -337,7 +357,25 @@ const rootCommand: Command<AppContext> = {
 | 
			
		||||
    ["showconfig", showConfigCommand],
 | 
			
		||||
  ]),
 | 
			
		||||
  action: ({ context }) => {
 | 
			
		||||
    context.print("Welcome to Access Control CLI");
 | 
			
		||||
    context.print([
 | 
			
		||||
      {
 | 
			
		||||
        text: "请使用 ",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        text: "@AC --help",
 | 
			
		||||
        clickEvent: {
 | 
			
		||||
          action: "copy_to_clipboard",
 | 
			
		||||
          value: "@AC --help",
 | 
			
		||||
        },
 | 
			
		||||
        hoverEvent: {
 | 
			
		||||
          action: "show_text",
 | 
			
		||||
          value: "点击复制命令",
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        text: " 获取门禁系统更详细的命令说明😊😊😊",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ interface UserGroupConfig {
 | 
			
		||||
  groupName: string;
 | 
			
		||||
  isAllowed: boolean;
 | 
			
		||||
  isNotice: boolean;
 | 
			
		||||
  isWelcome: boolean;
 | 
			
		||||
  groupUsers: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +40,7 @@ const defaultConfig: AccessConfig = {
 | 
			
		||||
    groupUsers: ["Selcon"],
 | 
			
		||||
    isAllowed: true,
 | 
			
		||||
    isNotice: true,
 | 
			
		||||
    isWelcome: true,
 | 
			
		||||
  },
 | 
			
		||||
  usersGroups: [
 | 
			
		||||
    {
 | 
			
		||||
@@ -46,57 +48,60 @@ const defaultConfig: AccessConfig = {
 | 
			
		||||
      groupUsers: [],
 | 
			
		||||
      isAllowed: true,
 | 
			
		||||
      isNotice: true,
 | 
			
		||||
      isWelcome: false,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      groupName: "VIP",
 | 
			
		||||
      groupUsers: [],
 | 
			
		||||
      isAllowed: true,
 | 
			
		||||
      isNotice: false,
 | 
			
		||||
      isWelcome: true,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      groupName: "enemies",
 | 
			
		||||
      groupUsers: [],
 | 
			
		||||
      isAllowed: false,
 | 
			
		||||
      isNotice: false,
 | 
			
		||||
      isWelcome: false,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  welcomeToastConfig: {
 | 
			
		||||
    title: {
 | 
			
		||||
      text: "Welcome",
 | 
			
		||||
      text: "欢迎",
 | 
			
		||||
      color: "green",
 | 
			
		||||
    },
 | 
			
		||||
    msg: {
 | 
			
		||||
      text: "Hello User %playerName%",
 | 
			
		||||
      color: "green",
 | 
			
		||||
      text: "欢迎 %playerName% 参观桃源星喵~",
 | 
			
		||||
      color: "#EDC8DA",
 | 
			
		||||
    },
 | 
			
		||||
    prefix: "Taohuayuan",
 | 
			
		||||
    brackets: "[]",
 | 
			
		||||
    prefix: "桃源星",
 | 
			
		||||
    brackets: "<>",
 | 
			
		||||
    bracketColor: "",
 | 
			
		||||
  },
 | 
			
		||||
  noticeToastConfig: {
 | 
			
		||||
    title: {
 | 
			
		||||
      text: "Notice",
 | 
			
		||||
      text: "警告",
 | 
			
		||||
      color: "red",
 | 
			
		||||
    },
 | 
			
		||||
    msg: {
 | 
			
		||||
      text: "Unfamiliar player %playerName% appeared at Position %playerPosX%, %playerPosY%, %playerPosZ%",
 | 
			
		||||
      text: "陌生玩家 %playerName% 出现在 %playerPosX%, %playerPosY%, %playerPosZ%",
 | 
			
		||||
      color: "red",
 | 
			
		||||
    },
 | 
			
		||||
    prefix: "Taohuayuan",
 | 
			
		||||
    brackets: "[]",
 | 
			
		||||
    prefix: "桃源星",
 | 
			
		||||
    brackets: "<>",
 | 
			
		||||
    bracketColor: "",
 | 
			
		||||
  },
 | 
			
		||||
  warnToastConfig: {
 | 
			
		||||
    title: {
 | 
			
		||||
      text: "Attention!!!",
 | 
			
		||||
      text: "注意",
 | 
			
		||||
      color: "red",
 | 
			
		||||
    },
 | 
			
		||||
    msg: {
 | 
			
		||||
      text: "%playerName% you are not allowed to be here",
 | 
			
		||||
      text: "%playerName% 你已经进入桃源星领地",
 | 
			
		||||
      color: "red",
 | 
			
		||||
    },
 | 
			
		||||
    prefix: "Taohuayuan",
 | 
			
		||||
    brackets: "[]",
 | 
			
		||||
    prefix: "桃源星",
 | 
			
		||||
    brackets: "<>",
 | 
			
		||||
    bracketColor: "",
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { deepCopy } from "@/lib/common";
 | 
			
		||||
import { ReadWriteLock } from "@/lib/mutex/ReadWriteLock";
 | 
			
		||||
import { ChatManager } from "@/lib/ChatManager";
 | 
			
		||||
import { gTimerManager } from "@/lib/TimerManager";
 | 
			
		||||
import { KeyEvent, pullEventAs } from "@/lib/event";
 | 
			
		||||
 | 
			
		||||
const args = [...$vararg];
 | 
			
		||||
 | 
			
		||||
@@ -25,17 +26,20 @@ logger.info("Load config successfully!");
 | 
			
		||||
logger.debug(textutils.serialise(config, { allow_repetitions: true }));
 | 
			
		||||
 | 
			
		||||
// Peripheral
 | 
			
		||||
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
 | 
			
		||||
const chatBox = peripheralManager.findByNameRequired("chatBox");
 | 
			
		||||
const playerDetector = peripheral.find(
 | 
			
		||||
  "playerDetector",
 | 
			
		||||
)[0] as PlayerDetectorPeripheral;
 | 
			
		||||
const chatBox = peripheral.find("chatBox")[0] as ChatBoxPeripheral;
 | 
			
		||||
const chatManager: ChatManager = new ChatManager([chatBox]);
 | 
			
		||||
 | 
			
		||||
// Global
 | 
			
		||||
let inRangePlayers: string[] = [];
 | 
			
		||||
let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
 | 
			
		||||
let gInRangePlayers: string[] = [];
 | 
			
		||||
let gWatchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
 | 
			
		||||
let gIsRunning = true;
 | 
			
		||||
 | 
			
		||||
interface ParseParams {
 | 
			
		||||
  name?: string;
 | 
			
		||||
  group?: string;
 | 
			
		||||
  playerName?: string;
 | 
			
		||||
  groupName?: string;
 | 
			
		||||
  info?: PlayerInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -47,8 +51,8 @@ function reloadConfig() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  config = loadConfig(configFilepath)!;
 | 
			
		||||
  inRangePlayers = [];
 | 
			
		||||
  watchPlayersInfo = [];
 | 
			
		||||
  gInRangePlayers = [];
 | 
			
		||||
  gWatchPlayersInfo = [];
 | 
			
		||||
  releaser.release();
 | 
			
		||||
  logger.info("Reload config successfully!");
 | 
			
		||||
}
 | 
			
		||||
@@ -56,7 +60,7 @@ function reloadConfig() {
 | 
			
		||||
function safeParseTextComponent(
 | 
			
		||||
  component: MinecraftTextComponent,
 | 
			
		||||
  params?: ParseParams,
 | 
			
		||||
): string {
 | 
			
		||||
): MinecraftTextComponent {
 | 
			
		||||
  const newComponent = deepCopy(component);
 | 
			
		||||
 | 
			
		||||
  if (newComponent.text == undefined) {
 | 
			
		||||
@@ -64,11 +68,11 @@ function safeParseTextComponent(
 | 
			
		||||
  } else if (newComponent.text.includes("%")) {
 | 
			
		||||
    newComponent.text = newComponent.text.replace(
 | 
			
		||||
      "%playerName%",
 | 
			
		||||
      params?.name ?? "UnknowPlayer",
 | 
			
		||||
      params?.playerName ?? "UnknowPlayer",
 | 
			
		||||
    );
 | 
			
		||||
    newComponent.text = newComponent.text.replace(
 | 
			
		||||
      "%groupName%",
 | 
			
		||||
      params?.group ?? "UnknowGroup",
 | 
			
		||||
      params?.groupName ?? "UnknowGroup",
 | 
			
		||||
    );
 | 
			
		||||
    newComponent.text = newComponent.text.replace(
 | 
			
		||||
      "%playerPosX%",
 | 
			
		||||
@@ -83,7 +87,34 @@ function safeParseTextComponent(
 | 
			
		||||
      params?.info?.z.toString() ?? "UnknowPosZ",
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  return textutils.serialiseJSON(newComponent);
 | 
			
		||||
  return newComponent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sendMessage(
 | 
			
		||||
  toastConfig: ToastConfig,
 | 
			
		||||
  targetPlayer: string,
 | 
			
		||||
  params: ParseParams,
 | 
			
		||||
) {
 | 
			
		||||
  let releaser = configLock.tryAcquireRead();
 | 
			
		||||
  while (releaser === undefined) {
 | 
			
		||||
    sleep(0.1);
 | 
			
		||||
    releaser = configLock.tryAcquireRead();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  chatManager.sendMessage({
 | 
			
		||||
    message: safeParseTextComponent(
 | 
			
		||||
      toastConfig.msg ?? config.welcomeToastConfig.msg,
 | 
			
		||||
      params,
 | 
			
		||||
    ),
 | 
			
		||||
    prefix: toastConfig.prefix ?? config.welcomeToastConfig.prefix,
 | 
			
		||||
    brackets: toastConfig.brackets ?? config.welcomeToastConfig.brackets,
 | 
			
		||||
    bracketColor:
 | 
			
		||||
      toastConfig.bracketColor ?? config.welcomeToastConfig.bracketColor,
 | 
			
		||||
    targetPlayer: targetPlayer,
 | 
			
		||||
    utf8Support: true,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  releaser.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sendToast(
 | 
			
		||||
@@ -134,7 +165,7 @@ function sendNotice(player: string, playerInfo?: PlayerInfo) {
 | 
			
		||||
  for (const targetPlayer of noticeTargetPlayers) {
 | 
			
		||||
    if (!onlinePlayers.includes(targetPlayer)) continue;
 | 
			
		||||
    sendToast(config.noticeToastConfig, targetPlayer, {
 | 
			
		||||
      name: player,
 | 
			
		||||
      playerName: player,
 | 
			
		||||
      info: playerInfo,
 | 
			
		||||
    });
 | 
			
		||||
    sleep(1);
 | 
			
		||||
@@ -152,10 +183,10 @@ function sendWarn(player: string) {
 | 
			
		||||
    releaser = configLock.tryAcquireRead();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendToast(config.warnToastConfig, player, { name: player });
 | 
			
		||||
  sendToast(config.warnToastConfig, player, { playerName: player });
 | 
			
		||||
  chatManager.sendMessage({
 | 
			
		||||
    message: safeParseTextComponent(config.warnToastConfig.msg, {
 | 
			
		||||
      name: player,
 | 
			
		||||
      playerName: player,
 | 
			
		||||
    }),
 | 
			
		||||
    targetPlayer: player,
 | 
			
		||||
    prefix: "AccessControl",
 | 
			
		||||
@@ -166,18 +197,18 @@ function sendWarn(player: string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function watchLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
  while (gIsRunning) {
 | 
			
		||||
    const releaser = configLock.tryAcquireRead();
 | 
			
		||||
    if (releaser === undefined) {
 | 
			
		||||
      os.sleep(1);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const watchPlayerNames = watchPlayersInfo.flatMap((value) => value.name);
 | 
			
		||||
    const watchPlayerNames = gWatchPlayersInfo.flatMap((value) => value.name);
 | 
			
		||||
    logger.debug(`Watch Players [ ${watchPlayerNames.join(", ")} ]`);
 | 
			
		||||
    for (const player of watchPlayersInfo) {
 | 
			
		||||
    for (const player of gWatchPlayersInfo) {
 | 
			
		||||
      const playerInfo = playerDetector.getPlayerPos(player.name);
 | 
			
		||||
      if (inRangePlayers.includes(player.name)) {
 | 
			
		||||
      if (gInRangePlayers.includes(player.name)) {
 | 
			
		||||
        // Notice
 | 
			
		||||
        if (player.hasNoticeTimes < config.noticeTimes) {
 | 
			
		||||
          sendNotice(player.name, playerInfo);
 | 
			
		||||
@@ -193,7 +224,7 @@ function watchLoop() {
 | 
			
		||||
        );
 | 
			
		||||
      } else {
 | 
			
		||||
        // Get rid of player from list
 | 
			
		||||
        watchPlayersInfo = watchPlayersInfo.filter(
 | 
			
		||||
        gWatchPlayersInfo = gWatchPlayersInfo.filter(
 | 
			
		||||
          (value) => value.name != player.name,
 | 
			
		||||
        );
 | 
			
		||||
        logger.info(
 | 
			
		||||
@@ -209,7 +240,7 @@ function watchLoop() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mainLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
  while (gIsRunning) {
 | 
			
		||||
    const releaser = configLock.tryAcquireRead();
 | 
			
		||||
    if (releaser === undefined) {
 | 
			
		||||
      os.sleep(0.1);
 | 
			
		||||
@@ -221,20 +252,31 @@ function mainLoop() {
 | 
			
		||||
    logger.debug(`Detected ${players.length} players: ${playersList}`);
 | 
			
		||||
 | 
			
		||||
    for (const player of players) {
 | 
			
		||||
      if (inRangePlayers.includes(player)) continue;
 | 
			
		||||
      if (gInRangePlayers.includes(player)) continue;
 | 
			
		||||
 | 
			
		||||
      // Get player Info
 | 
			
		||||
      const playerInfo = playerDetector.getPlayerPos(player);
 | 
			
		||||
 | 
			
		||||
      if (config.adminGroupConfig.groupUsers.includes(player)) {
 | 
			
		||||
        logger.info(`Admin ${player} appear`);
 | 
			
		||||
        logger.info(
 | 
			
		||||
          `Admin ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
 | 
			
		||||
        );
 | 
			
		||||
        if (config.adminGroupConfig.isWelcome)
 | 
			
		||||
          sendMessage(config.welcomeToastConfig, player, {
 | 
			
		||||
            playerName: player,
 | 
			
		||||
            groupName: "Admin",
 | 
			
		||||
            info: playerInfo,
 | 
			
		||||
          });
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // New player appear
 | 
			
		||||
      const playerInfo = playerDetector.getPlayerPos(player);
 | 
			
		||||
      let groupConfig: UserGroupConfig = {
 | 
			
		||||
        groupName: "Unfamiliar",
 | 
			
		||||
        groupUsers: [],
 | 
			
		||||
        isAllowed: false,
 | 
			
		||||
        isNotice: false,
 | 
			
		||||
        isWelcome: false,
 | 
			
		||||
      };
 | 
			
		||||
      for (const userGroupConfig of config.usersGroups) {
 | 
			
		||||
        if (userGroupConfig.groupUsers == undefined) continue;
 | 
			
		||||
@@ -247,28 +289,37 @@ function mainLoop() {
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (config.adminGroupConfig.isWelcome)
 | 
			
		||||
        sendMessage(config.welcomeToastConfig, player, {
 | 
			
		||||
          playerName: player,
 | 
			
		||||
          groupName: groupConfig.groupName,
 | 
			
		||||
          info: playerInfo,
 | 
			
		||||
        });
 | 
			
		||||
      if (groupConfig.isAllowed) continue;
 | 
			
		||||
 | 
			
		||||
      logger.warn(
 | 
			
		||||
        `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
 | 
			
		||||
      );
 | 
			
		||||
      if (config.isWarn) sendWarn(player);
 | 
			
		||||
      watchPlayersInfo = [
 | 
			
		||||
        ...watchPlayersInfo,
 | 
			
		||||
      gWatchPlayersInfo = [
 | 
			
		||||
        ...gWatchPlayersInfo,
 | 
			
		||||
        { name: player, hasNoticeTimes: 0 },
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inRangePlayers = players;
 | 
			
		||||
    gInRangePlayers = players;
 | 
			
		||||
    releaser.release();
 | 
			
		||||
    os.sleep(config.detectInterval);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function keyboardLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    const [eventType, key] = os.pullEvent("key");
 | 
			
		||||
    if (eventType === "key" && key === keys.c) {
 | 
			
		||||
  while (gIsRunning) {
 | 
			
		||||
    const event = pullEventAs(KeyEvent, "key");
 | 
			
		||||
    if (event === undefined) continue;
 | 
			
		||||
 | 
			
		||||
    if (event.key === keys.c) {
 | 
			
		||||
      logger.info("Launching Access Control TUI...");
 | 
			
		||||
      try {
 | 
			
		||||
        logger.setInTerminal(false);
 | 
			
		||||
@@ -280,7 +331,12 @@ function keyboardLoop() {
 | 
			
		||||
        logger.setInTerminal(true);
 | 
			
		||||
        reloadConfig();
 | 
			
		||||
      }
 | 
			
		||||
    } else if (event.key === keys.r) {
 | 
			
		||||
      reloadConfig();
 | 
			
		||||
    }
 | 
			
		||||
    // else if (event.key === keys.q) {
 | 
			
		||||
    //   gIsRunning = false;
 | 
			
		||||
    // }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -300,7 +356,7 @@ function cliLoop() {
 | 
			
		||||
      }),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
  while (gIsRunning) {
 | 
			
		||||
    const result = chatManager.getReceivedMessage();
 | 
			
		||||
    if (result.isErr()) {
 | 
			
		||||
      sleep(0.5);
 | 
			
		||||
@@ -342,9 +398,11 @@ function main(args: string[]) {
 | 
			
		||||
  logger.info("Starting access control system, get args: " + args.join(", "));
 | 
			
		||||
  if (args.length == 1) {
 | 
			
		||||
    if (args[0] == "start") {
 | 
			
		||||
      print(
 | 
			
		||||
        "Access Control System started. Press 'c' to open configuration TUI.",
 | 
			
		||||
      );
 | 
			
		||||
      const tutorial: string[] = [];
 | 
			
		||||
      tutorial.push("Access Control System started.");
 | 
			
		||||
      tutorial.push("\tPress 'c' to open configuration TUI.");
 | 
			
		||||
      tutorial.push("\tPress 'r' to reload configuration.");
 | 
			
		||||
      print(tutorial.join("\n"));
 | 
			
		||||
      parallel.waitForAll(
 | 
			
		||||
        () => mainLoop(),
 | 
			
		||||
        () => gTimerManager.run(),
 | 
			
		||||
 
 | 
			
		||||
@@ -355,6 +355,34 @@ const AccessControlTUI = () => {
 | 
			
		||||
        { class: "flex flex-col ml-2" },
 | 
			
		||||
        label({}, () => `Group: ${getSelectedGroup().groupName}`),
 | 
			
		||||
 | 
			
		||||
        div(
 | 
			
		||||
          { class: "flex flex-row" },
 | 
			
		||||
          label({}, "Is Welcome:"),
 | 
			
		||||
          input({
 | 
			
		||||
            type: "checkbox",
 | 
			
		||||
            checked: () => getSelectedGroup().isWelcome,
 | 
			
		||||
            onChange: (checked) => {
 | 
			
		||||
              const groupIndex = selectedGroupIndex();
 | 
			
		||||
              if (groupIndex === 0) {
 | 
			
		||||
                const currentAdmin = config().adminGroupConfig;
 | 
			
		||||
                setConfig("adminGroupConfig", {
 | 
			
		||||
                  ...currentAdmin,
 | 
			
		||||
                  isWelcome: checked,
 | 
			
		||||
                });
 | 
			
		||||
              } else {
 | 
			
		||||
                const actualIndex = groupIndex - 1;
 | 
			
		||||
                const currentGroups = config().usersGroups;
 | 
			
		||||
                const currentGroup = currentGroups[actualIndex];
 | 
			
		||||
                const newGroups = [...currentGroups];
 | 
			
		||||
                newGroups[actualIndex] = {
 | 
			
		||||
                  ...currentGroup,
 | 
			
		||||
                  isWelcome: checked,
 | 
			
		||||
                };
 | 
			
		||||
                setConfig("usersGroups", newGroups);
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
          }),
 | 
			
		||||
        ),
 | 
			
		||||
        div(
 | 
			
		||||
          { class: "flex flex-row" },
 | 
			
		||||
          label({}, "Is Allowed:"),
 | 
			
		||||
@@ -541,7 +569,12 @@ const AccessControlTUI = () => {
 | 
			
		||||
          label({}, "Prefix:"),
 | 
			
		||||
          input({
 | 
			
		||||
            type: "text",
 | 
			
		||||
            value: () => getTempToastConfig().prefix,
 | 
			
		||||
            value: () => {
 | 
			
		||||
              const str = textutils.serialiseJSON(getTempToastConfig().prefix, {
 | 
			
		||||
                unicode_strings: true,
 | 
			
		||||
              });
 | 
			
		||||
              return str.substring(1, str.length - 1);
 | 
			
		||||
            },
 | 
			
		||||
            onInput: (value) =>
 | 
			
		||||
              setTempToastConfig({ ...getTempToastConfig(), prefix: value }),
 | 
			
		||||
            onFocusChanged: () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ export interface ChatToast extends ChatBasicMessage {
 | 
			
		||||
  /** Target player username to send the toast to */
 | 
			
		||||
  targetPlayer: string;
 | 
			
		||||
  /** Title of the toast notification */
 | 
			
		||||
  title: string;
 | 
			
		||||
  title: string | MinecraftTextComponent | MinecraftTextComponent[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -224,9 +224,13 @@ export class ChatManager {
 | 
			
		||||
        // Send private message to specific player
 | 
			
		||||
        if (typeof message.message === "string") {
 | 
			
		||||
          [success, errorMsg] = chatbox.sendMessageToPlayer(
 | 
			
		||||
            message.message,
 | 
			
		||||
            textutils.serialiseJSON(message.message, {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            message.targetPlayer,
 | 
			
		||||
            message.prefix,
 | 
			
		||||
            textutils.serialiseJSON(message.prefix ?? "AP", {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            message.brackets,
 | 
			
		||||
            message.bracketColor,
 | 
			
		||||
            message.range,
 | 
			
		||||
@@ -236,11 +240,13 @@ export class ChatManager {
 | 
			
		||||
          // Handle MinecraftTextComponent for private message
 | 
			
		||||
          [success, errorMsg] = chatbox.sendFormattedMessageToPlayer(
 | 
			
		||||
            textutils.serialiseJSON(message.message, {
 | 
			
		||||
              unicode_strings: true,
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
              allow_repetitions: true,
 | 
			
		||||
            }),
 | 
			
		||||
            message.targetPlayer,
 | 
			
		||||
            message.prefix,
 | 
			
		||||
            textutils.serialiseJSON(message.prefix ?? "AP", {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            message.brackets,
 | 
			
		||||
            message.bracketColor,
 | 
			
		||||
            message.range,
 | 
			
		||||
@@ -251,8 +257,12 @@ export class ChatManager {
 | 
			
		||||
        // Send global message
 | 
			
		||||
        if (typeof message.message === "string") {
 | 
			
		||||
          [success, errorMsg] = chatbox.sendMessage(
 | 
			
		||||
            message.message,
 | 
			
		||||
            message.prefix,
 | 
			
		||||
            textutils.serialiseJSON(message.message, {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            textutils.serialiseJSON(message.prefix ?? "AP", {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            message.brackets,
 | 
			
		||||
            message.bracketColor,
 | 
			
		||||
            message.range,
 | 
			
		||||
@@ -262,10 +272,12 @@ export class ChatManager {
 | 
			
		||||
          // Handle MinecraftTextComponent for global message
 | 
			
		||||
          [success, errorMsg] = chatbox.sendFormattedMessage(
 | 
			
		||||
            textutils.serialiseJSON(message.message, {
 | 
			
		||||
              unicode_strings: true,
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
              allow_repetitions: true,
 | 
			
		||||
            }),
 | 
			
		||||
            message.prefix,
 | 
			
		||||
            textutils.serialiseJSON(message.prefix ?? "AP", {
 | 
			
		||||
              unicode_strings: message.utf8Support,
 | 
			
		||||
            }),
 | 
			
		||||
            message.brackets,
 | 
			
		||||
            message.bracketColor,
 | 
			
		||||
            message.range,
 | 
			
		||||
@@ -321,10 +333,16 @@ export class ChatManager {
 | 
			
		||||
        typeof toast.title === "string"
 | 
			
		||||
      ) {
 | 
			
		||||
        [success, errorMsg] = chatbox.sendToastToPlayer(
 | 
			
		||||
          toast.message,
 | 
			
		||||
          toast.title,
 | 
			
		||||
          textutils.serialiseJSON(toast.message, {
 | 
			
		||||
            unicode_strings: toast.utf8Support,
 | 
			
		||||
          }),
 | 
			
		||||
          textutils.serialiseJSON(toast.title, {
 | 
			
		||||
            unicode_strings: toast.utf8Support,
 | 
			
		||||
          }),
 | 
			
		||||
          toast.targetPlayer,
 | 
			
		||||
          toast.prefix,
 | 
			
		||||
          textutils.serialiseJSON(toast.prefix ?? "AP", {
 | 
			
		||||
            unicode_strings: toast.utf8Support,
 | 
			
		||||
          }),
 | 
			
		||||
          toast.brackets,
 | 
			
		||||
          toast.bracketColor,
 | 
			
		||||
          toast.range,
 | 
			
		||||
@@ -337,21 +355,23 @@ export class ChatManager {
 | 
			
		||||
            ? toast.message
 | 
			
		||||
            : textutils.serialiseJSON(toast.message, {
 | 
			
		||||
                unicode_strings: true,
 | 
			
		||||
                allow_repetitions: true,
 | 
			
		||||
                allow_repetitions: toast.utf8Support,
 | 
			
		||||
              });
 | 
			
		||||
        const titleJson =
 | 
			
		||||
          typeof toast.title === "string"
 | 
			
		||||
            ? toast.title
 | 
			
		||||
            : textutils.serialiseJSON(toast.title, {
 | 
			
		||||
                unicode_strings: true,
 | 
			
		||||
                allow_repetitions: true,
 | 
			
		||||
                allow_repetitions: toast.utf8Support,
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
        [success, errorMsg] = chatbox.sendFormattedToastToPlayer(
 | 
			
		||||
          messageJson,
 | 
			
		||||
          titleJson,
 | 
			
		||||
          toast.targetPlayer,
 | 
			
		||||
          toast.prefix,
 | 
			
		||||
          textutils.serialiseJSON(toast.prefix ?? "AP", {
 | 
			
		||||
            unicode_strings: toast.utf8Support,
 | 
			
		||||
          }),
 | 
			
		||||
          toast.brackets,
 | 
			
		||||
          toast.bracketColor,
 | 
			
		||||
          toast.range,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								types/advanced-peripherals/shared.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								types/advanced-peripherals/shared.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -30,7 +30,7 @@ declare type MinecraftColor =
 | 
			
		||||
  | "light_purple"
 | 
			
		||||
  | "yellow"
 | 
			
		||||
  | "white"
 | 
			
		||||
  | "reset"; // RGB color in #RRGGBB format
 | 
			
		||||
  | `#${string}`;
 | 
			
		||||
 | 
			
		||||
declare type MinecraftFont =
 | 
			
		||||
  | "minecraft:default"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								types/craftos/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								types/craftos/index.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -925,10 +925,22 @@ declare namespace textutils {
 | 
			
		||||
  function pagedTabulate(...args: (LuaTable | Object | Color)[]): void;
 | 
			
		||||
  function serialize(tab: object, options?: SerializeOptions): string;
 | 
			
		||||
  function serialise(tab: object, options?: SerializeOptions): string;
 | 
			
		||||
  function serializeJSON(tab: object, nbtStyle?: boolean): string;
 | 
			
		||||
  function serializeJSON(tab: object, options: SerializeJSONOptions): string;
 | 
			
		||||
  function serialiseJSON(tab: object, nbtStyle?: boolean): string;
 | 
			
		||||
  function serialiseJSON(tab: object, options: SerializeJSONOptions): string;
 | 
			
		||||
  function serializeJSON(
 | 
			
		||||
    tab: object | string | number | boolean,
 | 
			
		||||
    nbtStyle?: boolean,
 | 
			
		||||
  ): string;
 | 
			
		||||
  function serializeJSON(
 | 
			
		||||
    tab: object | string | number | boolean,
 | 
			
		||||
    options: SerializeJSONOptions,
 | 
			
		||||
  ): string;
 | 
			
		||||
  function serialiseJSON(
 | 
			
		||||
    tab: object | string | number | boolean,
 | 
			
		||||
    nbtStyle?: boolean,
 | 
			
		||||
  ): string;
 | 
			
		||||
  function serialiseJSON(
 | 
			
		||||
    tab: object | string | number | boolean,
 | 
			
		||||
    options: SerializeJSONOptions,
 | 
			
		||||
  ): string;
 | 
			
		||||
  function unserialize(str: string): unknown;
 | 
			
		||||
  function unserialise(str: string): unknown;
 | 
			
		||||
  function unserializeJSON(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user