mirror of
				https://github.com/SikongJueluo/cc-utils.git
				synced 2025-11-04 19:27:50 +08:00 
			
		
		
		
	fix: auto reload
reconstruct: semaphore and read write lock use undefined instead of null fix: accesscontrol auto reload config
This commit is contained in:
		@@ -1,5 +1,10 @@
 | 
			
		||||
import { CCLog } from "@/lib/ccLog";
 | 
			
		||||
import { AccessConfig, UserGroupConfig, saveConfig } from "./config";
 | 
			
		||||
import {
 | 
			
		||||
  AccessConfig,
 | 
			
		||||
  UserGroupConfig,
 | 
			
		||||
  loadConfig,
 | 
			
		||||
  saveConfig,
 | 
			
		||||
} from "./config";
 | 
			
		||||
import { ChatBoxEvent, pullEventAs } from "@/lib/event";
 | 
			
		||||
import { parseBoolean } from "@/lib/common";
 | 
			
		||||
 | 
			
		||||
@@ -16,19 +21,19 @@ interface CLIResult {
 | 
			
		||||
  success: boolean;
 | 
			
		||||
  message?: string;
 | 
			
		||||
  shouldSaveConfig?: boolean;
 | 
			
		||||
  config?: AccessConfig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CLI上下文
 | 
			
		||||
interface CLIContext {
 | 
			
		||||
  config: AccessConfig;
 | 
			
		||||
  configFilepath: string;
 | 
			
		||||
  reloadConfig: () => void;
 | 
			
		||||
  log: CCLog;
 | 
			
		||||
  chatBox: ChatBoxPeripheral;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getGroupNames(context: CLIContext) {
 | 
			
		||||
  return context.config.usersGroups.flatMap((value) => value.groupName);
 | 
			
		||||
function getGroupNames(config: AccessConfig) {
 | 
			
		||||
  return config.usersGroups.flatMap((value) => value.groupName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 基础命令处理器
 | 
			
		||||
@@ -76,10 +81,6 @@ class CLICommandProcessor {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const ret = command.execute(args, executor, this.context);
 | 
			
		||||
    if (ret.success) {
 | 
			
		||||
      this.context.reloadConfig();
 | 
			
		||||
      return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +102,8 @@ class CLICommandProcessor {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (result.shouldSaveConfig === true) {
 | 
			
		||||
      saveConfig(this.context.config, this.context.configFilepath);
 | 
			
		||||
      saveConfig(result.config!, this.context.configFilepath);
 | 
			
		||||
      this.context.reloadConfig();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -121,26 +123,30 @@ class AddCommand implements CLICommand {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [groupName, playerName] = args;
 | 
			
		||||
    const config: AccessConfig = loadConfig(context.configFilepath)!;
 | 
			
		||||
 | 
			
		||||
    if (groupName === "admin") {
 | 
			
		||||
      context.config.adminGroupConfig.groupUsers.push(playerName);
 | 
			
		||||
      config.adminGroupConfig.groupUsers.push(playerName);
 | 
			
		||||
      return {
 | 
			
		||||
        success: true,
 | 
			
		||||
        message: `Add player ${playerName} to admin`,
 | 
			
		||||
        shouldSaveConfig: true,
 | 
			
		||||
        config,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const groupNames = getGroupNames(context);
 | 
			
		||||
    const groupNames = getGroupNames(config);
 | 
			
		||||
 | 
			
		||||
    if (!groupNames.includes(groupName)) {
 | 
			
		||||
      return {
 | 
			
		||||
        success: false,
 | 
			
		||||
        message: `Invalid group: ${groupName}. Available groups: ${groupNames.join(", ")}`,
 | 
			
		||||
        message: `Invalid group: ${groupName}. Available groups: ${groupNames.join(
 | 
			
		||||
          ", ",
 | 
			
		||||
        )}`,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const groupConfig = context.config.usersGroups.find(
 | 
			
		||||
    const groupConfig = config.usersGroups.find(
 | 
			
		||||
      (value) => value.groupName === groupName,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@@ -161,6 +167,7 @@ class AddCommand implements CLICommand {
 | 
			
		||||
      success: true,
 | 
			
		||||
      message: `Add player ${playerName} to ${groupConfig.groupName}`,
 | 
			
		||||
      shouldSaveConfig: true,
 | 
			
		||||
      config,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -188,16 +195,19 @@ class DelCommand implements CLICommand {
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const groupNames = getGroupNames(context);
 | 
			
		||||
    const config: AccessConfig = loadConfig(context.configFilepath)!;
 | 
			
		||||
    const groupNames = getGroupNames(config);
 | 
			
		||||
 | 
			
		||||
    if (!groupNames.includes(groupName)) {
 | 
			
		||||
      return {
 | 
			
		||||
        success: false,
 | 
			
		||||
        message: `Invalid group: ${groupName}. Available groups: ${groupNames.join(", ")}`,
 | 
			
		||||
        message: `Invalid group: ${groupName}. Available groups: ${groupNames.join(
 | 
			
		||||
          ", ",
 | 
			
		||||
        )}`,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const groupConfig = context.config.usersGroups.find(
 | 
			
		||||
    const groupConfig = config.usersGroups.find(
 | 
			
		||||
      (value) => value.groupName === groupName,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@@ -220,6 +230,7 @@ class DelCommand implements CLICommand {
 | 
			
		||||
      success: true,
 | 
			
		||||
      message: `Delete ${groupConfig.groupName} ${playerName}`,
 | 
			
		||||
      shouldSaveConfig: true,
 | 
			
		||||
      config,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -231,9 +242,10 @@ class ListCommand implements CLICommand {
 | 
			
		||||
  usage = "list";
 | 
			
		||||
 | 
			
		||||
  execute(_args: string[], _executor: string, context: CLIContext): CLIResult {
 | 
			
		||||
    let message = `Admins : [ ${context.config.adminGroupConfig.groupUsers.join(", ")} ]\n`;
 | 
			
		||||
    const config = loadConfig(context.configFilepath)!;
 | 
			
		||||
    let message = `Admins : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]\n`;
 | 
			
		||||
 | 
			
		||||
    for (const groupConfig of context.config.usersGroups) {
 | 
			
		||||
    for (const groupConfig of config.usersGroups) {
 | 
			
		||||
      const users = groupConfig.groupUsers ?? [];
 | 
			
		||||
      message += `${groupConfig.groupName} : [ ${users.join(", ")} ]\n`;
 | 
			
		||||
    }
 | 
			
		||||
@@ -269,29 +281,34 @@ class SetCommand implements CLICommand {
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const config: AccessConfig = loadConfig(context.configFilepath)!;
 | 
			
		||||
 | 
			
		||||
    switch (option) {
 | 
			
		||||
      case "warnInterval":
 | 
			
		||||
        context.config.watchInterval = value;
 | 
			
		||||
        config.watchInterval = value;
 | 
			
		||||
        return {
 | 
			
		||||
          success: true,
 | 
			
		||||
          message: `Set warn interval to ${context.config.watchInterval}`,
 | 
			
		||||
          message: `Set warn interval to ${config.watchInterval}`,
 | 
			
		||||
          shouldSaveConfig: true,
 | 
			
		||||
          config,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      case "detectInterval":
 | 
			
		||||
        context.config.detectInterval = value;
 | 
			
		||||
        config.detectInterval = value;
 | 
			
		||||
        return {
 | 
			
		||||
          success: true,
 | 
			
		||||
          message: `Set detect interval to ${context.config.detectInterval}`,
 | 
			
		||||
          message: `Set detect interval to ${config.detectInterval}`,
 | 
			
		||||
          shouldSaveConfig: true,
 | 
			
		||||
          config,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      case "detectRange":
 | 
			
		||||
        context.config.detectRange = value;
 | 
			
		||||
        config.detectRange = value;
 | 
			
		||||
        return {
 | 
			
		||||
          success: true,
 | 
			
		||||
          message: `Set detect range to ${context.config.detectRange}`,
 | 
			
		||||
          message: `Set detect range to ${config.detectRange}`,
 | 
			
		||||
          shouldSaveConfig: true,
 | 
			
		||||
          config,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
@@ -310,7 +327,8 @@ class HelpCommand implements CLICommand {
 | 
			
		||||
  usage = "help";
 | 
			
		||||
 | 
			
		||||
  execute(_args: string[], _executor: string, context: CLIContext): CLIResult {
 | 
			
		||||
    const groupNames = getGroupNames(context);
 | 
			
		||||
    const config = loadConfig(context.configFilepath)!;
 | 
			
		||||
    const groupNames = getGroupNames(config);
 | 
			
		||||
    const helpMessage = `
 | 
			
		||||
Command Usage: @AC /<Command> [args]
 | 
			
		||||
Commands:
 | 
			
		||||
@@ -378,13 +396,14 @@ class EditCommand implements CLICommand {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [groupName, property, valueStr] = args;
 | 
			
		||||
    const config: AccessConfig = loadConfig(context.configFilepath)!;
 | 
			
		||||
 | 
			
		||||
    let groupConfig: UserGroupConfig | undefined;
 | 
			
		||||
 | 
			
		||||
    if (groupName === "admin") {
 | 
			
		||||
      groupConfig = context.config.adminGroupConfig;
 | 
			
		||||
      groupConfig = config.adminGroupConfig;
 | 
			
		||||
    } else {
 | 
			
		||||
      groupConfig = context.config.usersGroups.find(
 | 
			
		||||
      groupConfig = config.usersGroups.find(
 | 
			
		||||
        (group) => group.groupName === groupName,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
@@ -405,6 +424,7 @@ class EditCommand implements CLICommand {
 | 
			
		||||
            success: true,
 | 
			
		||||
            message: `Set ${groupName}.isAllowed to ${groupConfig.isAllowed}`,
 | 
			
		||||
            shouldSaveConfig: true,
 | 
			
		||||
            config,
 | 
			
		||||
          };
 | 
			
		||||
        } else {
 | 
			
		||||
          return {
 | 
			
		||||
@@ -423,6 +443,7 @@ class EditCommand implements CLICommand {
 | 
			
		||||
            success: true,
 | 
			
		||||
            message: `Set ${groupName}.isNotice to ${groupConfig.isNotice}`,
 | 
			
		||||
            shouldSaveConfig: true,
 | 
			
		||||
            config,
 | 
			
		||||
          };
 | 
			
		||||
        } else {
 | 
			
		||||
          return {
 | 
			
		||||
@@ -450,15 +471,16 @@ class ShowConfigCommand implements CLICommand {
 | 
			
		||||
 | 
			
		||||
  execute(args: string[], _executor: string, context: CLIContext): CLIResult {
 | 
			
		||||
    const type = args[0] || "all";
 | 
			
		||||
    const config = loadConfig(context.configFilepath)!;
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case "groups": {
 | 
			
		||||
        let groupsMessage = `Admin Group: ${context.config.adminGroupConfig.groupName}\n`;
 | 
			
		||||
        groupsMessage += `  Users: [${context.config.adminGroupConfig.groupUsers.join(", ")}]\n`;
 | 
			
		||||
        groupsMessage += `  Allowed: ${context.config.adminGroupConfig.isAllowed}\n`;
 | 
			
		||||
        groupsMessage += `  notice: ${context.config.adminGroupConfig.isNotice}\n\n`;
 | 
			
		||||
        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`;
 | 
			
		||||
 | 
			
		||||
        for (const group of context.config.usersGroups) {
 | 
			
		||||
        for (const group of config.usersGroups) {
 | 
			
		||||
          groupsMessage += `Group: ${group.groupName}\n`;
 | 
			
		||||
          groupsMessage += `  Users: [${(group.groupUsers ?? []).join(", ")}]\n`;
 | 
			
		||||
          groupsMessage += `  Allowed: ${group.isAllowed}\n`;
 | 
			
		||||
@@ -474,18 +496,18 @@ class ShowConfigCommand implements CLICommand {
 | 
			
		||||
 | 
			
		||||
      case "toast": {
 | 
			
		||||
        let toastMessage = "Default Toast Config:\n";
 | 
			
		||||
        toastMessage += `  Title: ${context.config.welcomeToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  Message: ${context.config.welcomeToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  Prefix: ${context.config.welcomeToastConfig.prefix ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Brackets: ${context.config.welcomeToastConfig.brackets ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Bracket Color: ${context.config.welcomeToastConfig.bracketColor ?? "none"}\n\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`;
 | 
			
		||||
 | 
			
		||||
        toastMessage += "Warn Toast Config:\n";
 | 
			
		||||
        toastMessage += `  Title: ${context.config.warnToastConfig.title.text}\n`;
 | 
			
		||||
        toastMessage += `  Message: ${context.config.warnToastConfig.msg.text}\n`;
 | 
			
		||||
        toastMessage += `  Prefix: ${context.config.warnToastConfig.prefix ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Brackets: ${context.config.warnToastConfig.brackets ?? "none"}\n`;
 | 
			
		||||
        toastMessage += `  Bracket Color: ${context.config.warnToastConfig.bracketColor ?? "none"}`;
 | 
			
		||||
        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"}`;
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          success: true,
 | 
			
		||||
@@ -494,9 +516,9 @@ class ShowConfigCommand implements CLICommand {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case "all": {
 | 
			
		||||
        let allMessage = `Detect Range: ${context.config.detectRange}\n`;
 | 
			
		||||
        allMessage += `Detect Interval: ${context.config.detectInterval}\n`;
 | 
			
		||||
        allMessage += `Warn Interval: ${context.config.watchInterval}\n\n`;
 | 
			
		||||
        let allMessage = `Detect Range: ${config.detectRange}\n`;
 | 
			
		||||
        allMessage += `Detect Interval: ${config.detectInterval}\n`;
 | 
			
		||||
        allMessage += `Warn Interval: ${config.watchInterval}\n\n`;
 | 
			
		||||
        allMessage +=
 | 
			
		||||
          "Use 'showconfig groups' or 'showconfig toast' for detailed view";
 | 
			
		||||
 | 
			
		||||
@@ -530,10 +552,9 @@ export class AccessControlCLI {
 | 
			
		||||
      const ev = pullEventAs(ChatBoxEvent, "chat");
 | 
			
		||||
 | 
			
		||||
      if (ev === undefined) continue;
 | 
			
		||||
      if (
 | 
			
		||||
        !this.context.config.adminGroupConfig.groupUsers.includes(ev.username)
 | 
			
		||||
      )
 | 
			
		||||
        continue;
 | 
			
		||||
 | 
			
		||||
      const config = loadConfig(this.context.configFilepath)!;
 | 
			
		||||
      if (!config.adminGroupConfig.groupUsers.includes(ev.username)) continue;
 | 
			
		||||
      if (!ev.message.startsWith("@AC")) continue;
 | 
			
		||||
 | 
			
		||||
      this.context.log.info(
 | 
			
		||||
@@ -542,7 +563,6 @@ export class AccessControlCLI {
 | 
			
		||||
 | 
			
		||||
      const result = this.processor.processCommand(ev.message, ev.username);
 | 
			
		||||
      this.processor.sendResponse(result, ev.username);
 | 
			
		||||
 | 
			
		||||
      if (!result.success) {
 | 
			
		||||
        this.context.log.warn(`Command failed: ${result.message}`);
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -101,9 +101,13 @@ const defaultConfig: AccessConfig = {
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function loadConfig(filepath: string): AccessConfig {
 | 
			
		||||
function loadConfig(
 | 
			
		||||
  filepath: string,
 | 
			
		||||
  useDefault = true,
 | 
			
		||||
): AccessConfig | undefined {
 | 
			
		||||
  const [fp] = io.open(filepath, "r");
 | 
			
		||||
  if (fp == undefined) {
 | 
			
		||||
    if (useDefault === false) return undefined;
 | 
			
		||||
    print("Failed to open config file " + filepath);
 | 
			
		||||
    print("Use default config");
 | 
			
		||||
    saveConfig(defaultConfig, filepath);
 | 
			
		||||
@@ -112,6 +116,7 @@ function loadConfig(filepath: string): AccessConfig {
 | 
			
		||||
 | 
			
		||||
  const configJson = fp.read("*a");
 | 
			
		||||
  if (configJson == undefined) {
 | 
			
		||||
    if (useDefault === false) return undefined;
 | 
			
		||||
    print("Failed to read config file");
 | 
			
		||||
    print("Use default config");
 | 
			
		||||
    saveConfig(defaultConfig, filepath);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import { createAccessControlCLI } from "./cli";
 | 
			
		||||
import { launchAccessControlTUI } from "./tui";
 | 
			
		||||
import * as peripheralManager from "../lib/PeripheralManager";
 | 
			
		||||
import { deepCopy } from "@/lib/common";
 | 
			
		||||
import { ReadWriteLock } from "@/lib/ReadWriteLock";
 | 
			
		||||
 | 
			
		||||
const args = [...$vararg];
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +17,8 @@ const logger = new CCLog("accesscontrol.log", {
 | 
			
		||||
 | 
			
		||||
// Load Config
 | 
			
		||||
const configFilepath = `${shell.dir()}/access.config.json`;
 | 
			
		||||
let config = loadConfig(configFilepath);
 | 
			
		||||
let config = loadConfig(configFilepath)!;
 | 
			
		||||
const configLock = new ReadWriteLock();
 | 
			
		||||
logger.info("Load config successfully!");
 | 
			
		||||
logger.debug(textutils.serialise(config, { allow_repetitions: true }));
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +27,6 @@ const playerDetector = peripheralManager.findByNameRequired("playerDetector");
 | 
			
		||||
const chatBox = peripheralManager.findByNameRequired("chatBox");
 | 
			
		||||
 | 
			
		||||
// Global
 | 
			
		||||
let noticeTargetPlayers: string[];
 | 
			
		||||
let inRangePlayers: string[] = [];
 | 
			
		||||
let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
 | 
			
		||||
 | 
			
		||||
@@ -36,9 +37,16 @@ interface ParseParams {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reloadConfig() {
 | 
			
		||||
  config = loadConfig(configFilepath);
 | 
			
		||||
  let releaser = configLock.tryAcquireWrite();
 | 
			
		||||
  while (releaser === undefined) {
 | 
			
		||||
    sleep(1);
 | 
			
		||||
    releaser = configLock.tryAcquireWrite();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  config = loadConfig(configFilepath)!;
 | 
			
		||||
  inRangePlayers = [];
 | 
			
		||||
  watchPlayersInfo = [];
 | 
			
		||||
  releaser.release();
 | 
			
		||||
  logger.info("Reload config successfully!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +88,13 @@ function sendToast(
 | 
			
		||||
  targetPlayer: string,
 | 
			
		||||
  params: ParseParams,
 | 
			
		||||
) {
 | 
			
		||||
  return chatBox.sendFormattedToastToPlayer(
 | 
			
		||||
  let releaser = configLock.tryAcquireRead();
 | 
			
		||||
  while (releaser === undefined) {
 | 
			
		||||
    sleep(0.1);
 | 
			
		||||
    releaser = configLock.tryAcquireRead();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  chatBox.sendFormattedToastToPlayer(
 | 
			
		||||
    safeParseTextComponent(
 | 
			
		||||
      toastConfig.msg ?? config.welcomeToastConfig.msg,
 | 
			
		||||
      params,
 | 
			
		||||
@@ -96,11 +110,18 @@ function sendToast(
 | 
			
		||||
    undefined,
 | 
			
		||||
    true,
 | 
			
		||||
  );
 | 
			
		||||
  releaser.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sendNotice(player: string, playerInfo?: PlayerInfo) {
 | 
			
		||||
  let releaser = configLock.tryAcquireRead();
 | 
			
		||||
  while (releaser === undefined) {
 | 
			
		||||
    sleep(0.1);
 | 
			
		||||
    releaser = configLock.tryAcquireRead();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const onlinePlayers = playerDetector.getOnlinePlayers();
 | 
			
		||||
  noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat(
 | 
			
		||||
  const noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat(
 | 
			
		||||
    config.usersGroups
 | 
			
		||||
      .filter((value) => value.isNotice)
 | 
			
		||||
      .map((value) => value.groupUsers ?? [])
 | 
			
		||||
@@ -114,12 +135,19 @@ function sendNotice(player: string, playerInfo?: PlayerInfo) {
 | 
			
		||||
      info: playerInfo,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  releaser.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sendWarn(player: string) {
 | 
			
		||||
  const warnMsg = `Not Allowed Player ${player} Break in Home `;
 | 
			
		||||
  logger.warn(warnMsg);
 | 
			
		||||
 | 
			
		||||
  let releaser = configLock.tryAcquireRead();
 | 
			
		||||
  while (releaser === undefined) {
 | 
			
		||||
    sleep(0.1);
 | 
			
		||||
    releaser = configLock.tryAcquireRead();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendToast(config.warnToastConfig, player, { name: player });
 | 
			
		||||
  chatBox.sendFormattedMessageToPlayer(
 | 
			
		||||
    safeParseTextComponent(config.warnToastConfig.msg, { name: player }),
 | 
			
		||||
@@ -130,10 +158,17 @@ function sendWarn(player: string) {
 | 
			
		||||
    undefined,
 | 
			
		||||
    true,
 | 
			
		||||
  );
 | 
			
		||||
  releaser.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function watchLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    const releaser = configLock.tryAcquireRead();
 | 
			
		||||
    if (releaser === undefined) {
 | 
			
		||||
      os.sleep(1);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const watchPlayerNames = watchPlayersInfo.flatMap((value) => value.name);
 | 
			
		||||
    logger.debug(`Watch Players [ ${watchPlayerNames.join(", ")} ]`);
 | 
			
		||||
    for (const player of watchPlayersInfo) {
 | 
			
		||||
@@ -164,12 +199,19 @@ function watchLoop() {
 | 
			
		||||
      os.sleep(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    releaser.release();
 | 
			
		||||
    os.sleep(config.watchInterval);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mainLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    const releaser = configLock.tryAcquireRead();
 | 
			
		||||
    if (releaser === undefined) {
 | 
			
		||||
      os.sleep(0.1);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const players = playerDetector.getPlayersInRange(config.detectRange);
 | 
			
		||||
    const playersList = "[ " + players.join(",") + " ]";
 | 
			
		||||
    logger.debug(`Detected ${players.length} players: ${playersList}`);
 | 
			
		||||
@@ -214,6 +256,7 @@ function mainLoop() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inRangePlayers = players;
 | 
			
		||||
    releaser.release();
 | 
			
		||||
    os.sleep(config.detectInterval);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -232,7 +275,6 @@ function keyboardLoop() {
 | 
			
		||||
      } finally {
 | 
			
		||||
        logger.setInTerminal(true);
 | 
			
		||||
        reloadConfig();
 | 
			
		||||
        logger.info("Reload config successfully!");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -244,7 +286,6 @@ function main(args: string[]) {
 | 
			
		||||
    if (args[0] == "start") {
 | 
			
		||||
      // 创建CLI处理器
 | 
			
		||||
      const cli = createAccessControlCLI({
 | 
			
		||||
        config: config,
 | 
			
		||||
        configFilepath: configFilepath,
 | 
			
		||||
        reloadConfig: () => reloadConfig(),
 | 
			
		||||
        log: logger,
 | 
			
		||||
@@ -259,7 +300,7 @@ function main(args: string[]) {
 | 
			
		||||
          mainLoop();
 | 
			
		||||
        },
 | 
			
		||||
        () => {
 | 
			
		||||
          void cli.startConfigLoop();
 | 
			
		||||
          cli.startConfigLoop();
 | 
			
		||||
        },
 | 
			
		||||
        () => {
 | 
			
		||||
          watchLoop();
 | 
			
		||||
@@ -268,6 +309,7 @@ function main(args: string[]) {
 | 
			
		||||
          keyboardLoop();
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    } else if (args[0] == "config") {
 | 
			
		||||
      logger.info("Launching Access Control TUI...");
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ interface ErrorState {
 | 
			
		||||
const AccessControlTUI = () => {
 | 
			
		||||
  // Load configuration on initialization
 | 
			
		||||
  const configFilepath = `${shell.dir()}/access.config.json`;
 | 
			
		||||
  const loadedConfig = loadConfig(configFilepath);
 | 
			
		||||
  const loadedConfig = loadConfig(configFilepath)!;
 | 
			
		||||
  // Configuration state
 | 
			
		||||
  const [config, setConfig] = createStore<AccessConfig>(loadedConfig);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,11 +46,11 @@ export class ReadWriteLock {
 | 
			
		||||
  /**
 | 
			
		||||
   * Tries to acquire a read lock immediately. Returns null if not available.
 | 
			
		||||
   */
 | 
			
		||||
  tryAcquireRead(): ReadLockHandle | null {
 | 
			
		||||
  tryAcquireRead(): ReadLockHandle | undefined {
 | 
			
		||||
    const release = this._semaphore.tryAcquire(1);
 | 
			
		||||
 | 
			
		||||
    if (release === null) {
 | 
			
		||||
      return null;
 | 
			
		||||
    if (release === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { release };
 | 
			
		||||
@@ -72,11 +72,11 @@ export class ReadWriteLock {
 | 
			
		||||
  /**
 | 
			
		||||
   * Tries to acquire a write lock immediately. Returns null if not available.
 | 
			
		||||
   */
 | 
			
		||||
  tryAcquireWrite(): WriteLockHandle | null {
 | 
			
		||||
  tryAcquireWrite(): WriteLockHandle | undefined {
 | 
			
		||||
    const release = this._semaphore.tryAcquire(this._writerWeight);
 | 
			
		||||
 | 
			
		||||
    if (release === null) {
 | 
			
		||||
      return null;
 | 
			
		||||
    if (release === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { release };
 | 
			
		||||
 
 | 
			
		||||
@@ -45,13 +45,13 @@ export class Semaphore {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tryAcquire(weight = 1): Releaser | null {
 | 
			
		||||
  tryAcquire(weight = 1): Releaser | undefined {
 | 
			
		||||
    if (weight <= 0) {
 | 
			
		||||
      throw new Error(`invalid weight ${weight}: must be positive`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (weight > this._value || this._queue.toArray().length > 0) {
 | 
			
		||||
      return null;
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._value -= weight;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user