fix: notice toast; feature: auto-reload, deepcopy

fix: notcie toast only show the first one player
feature:
- auto reload config when start (maybe conflict in multi threads)
- add basic deepcopy function in common
This commit is contained in:
2025-10-15 23:22:08 +08:00
parent b9ce947b9b
commit 1fe3052e5d
2 changed files with 62 additions and 15 deletions

View File

@@ -3,6 +3,7 @@ import { ToastConfig, UserGroupConfig, loadConfig } from "./config";
import { createAccessControlCLI } from "./cli"; import { createAccessControlCLI } from "./cli";
import { launchAccessControlTUI } from "./tui"; import { launchAccessControlTUI } from "./tui";
import * as peripheralManager from "../lib/PeripheralManager"; import * as peripheralManager from "../lib/PeripheralManager";
import { deepCopy } from "@/lib/common";
const DEBUG = false; const DEBUG = false;
const args = [...$vararg]; const args = [...$vararg];
@@ -12,15 +13,18 @@ const logger = new CCLog("accesscontrol.log", true, DAY);
// Load Config // Load Config
const configFilepath = `${shell.dir()}/access.config.json`; const configFilepath = `${shell.dir()}/access.config.json`;
const config = loadConfig(configFilepath); let config = loadConfig(configFilepath);
logger.info("Load config successfully!"); logger.info("Load config successfully!");
if (DEBUG) if (DEBUG)
logger.debug(textutils.serialise(config, { allow_repetitions: true })); logger.debug(textutils.serialise(config, { allow_repetitions: true }));
const groupNames = config.usersGroups.map((value) => value.groupName); const groupNames = config.usersGroups.map((value) => value.groupName);
let noticeTargetPlayers: string[];
// Peripheral
const playerDetector = peripheralManager.findByNameRequired("playerDetector"); const playerDetector = peripheralManager.findByNameRequired("playerDetector");
const chatBox = peripheralManager.findByNameRequired("chatBox"); const chatBox = peripheralManager.findByNameRequired("chatBox");
// Global
let noticeTargetPlayers: string[];
let inRangePlayers: string[] = []; let inRangePlayers: string[] = [];
let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = []; let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
@@ -34,31 +38,33 @@ function safeParseTextComponent(
component: MinecraftTextComponent, component: MinecraftTextComponent,
params?: ParseParams, params?: ParseParams,
): string { ): string {
if (component.text == undefined) { const newComponent = deepCopy(component);
component.text = "Wrong text, please contanct with admin";
} else if (component.text.includes("%")) { if (newComponent.text == undefined) {
component.text = component.text.replace( newComponent.text = "Wrong text, please contanct with admin";
} else if (newComponent.text.includes("%")) {
newComponent.text = newComponent.text.replace(
"%playerName%", "%playerName%",
params?.name ?? "UnknowPlayer", params?.name ?? "UnknowPlayer",
); );
component.text = component.text.replace( newComponent.text = newComponent.text.replace(
"%groupName%", "%groupName%",
params?.group ?? "UnknowGroup", params?.group ?? "UnknowGroup",
); );
component.text = component.text.replace( newComponent.text = newComponent.text.replace(
"%playerPosX%", "%playerPosX%",
params?.info?.x.toString() ?? "UnknowPosX", params?.info?.x.toString() ?? "UnknowPosX",
); );
component.text = component.text.replace( newComponent.text = newComponent.text.replace(
"%playerPosY%", "%playerPosY%",
params?.info?.y.toString() ?? "UnknowPosY", params?.info?.y.toString() ?? "UnknowPosY",
); );
component.text = component.text.replace( newComponent.text = newComponent.text.replace(
"%playerPosZ%", "%playerPosZ%",
params?.info?.z.toString() ?? "UnknowPosZ", params?.info?.z.toString() ?? "UnknowPosZ",
); );
} }
return textutils.serialiseJSON(component); return textutils.serialiseJSON(newComponent);
} }
function sendToast( function sendToast(
@@ -120,10 +126,13 @@ function sendWarn(player: string) {
function watchLoop() { function watchLoop() {
while (true) { while (true) {
if (DEBUG) {
const watchPlayerNames = watchPlayersInfo.flatMap((value) => value.name);
logger.debug(`Watch Players [ ${watchPlayerNames.join(", ")} ]`);
}
for (const player of watchPlayersInfo) { for (const player of watchPlayersInfo) {
if (inRangePlayers.includes(player.name)) {
const playerInfo = playerDetector.getPlayerPos(player.name); const playerInfo = playerDetector.getPlayerPos(player.name);
if (inRangePlayers.includes(player.name)) {
// Notice // Notice
if (player.hasNoticeTimes < config.noticeTimes) { if (player.hasNoticeTimes < config.noticeTimes) {
sendNotice(player.name, playerInfo); sendNotice(player.name, playerInfo);
@@ -142,8 +151,11 @@ function watchLoop() {
watchPlayersInfo = watchPlayersInfo.filter( watchPlayersInfo = watchPlayersInfo.filter(
(value) => value.name != player.name, (value) => value.name != player.name,
); );
logger.info(`${player.name} has left the range`); logger.info(
`${player.name} has left the range at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
);
} }
os.sleep(3);
} }
os.sleep(config.watchInterval); os.sleep(config.watchInterval);
@@ -191,7 +203,10 @@ function mainLoop() {
`${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
); );
if (config.isWarn) sendWarn(player); if (config.isWarn) sendWarn(player);
watchPlayersInfo.push({ name: player, hasNoticeTimes: 0 }); watchPlayersInfo = [
...watchPlayersInfo,
{ name: player, hasNoticeTimes: 0 },
];
} }
inRangePlayers = players; inRangePlayers = players;
@@ -212,6 +227,8 @@ function keyboardLoop() {
logger.error(`TUI error: ${textutils.serialise(error as object)}`); logger.error(`TUI error: ${textutils.serialise(error as object)}`);
} finally { } finally {
logger.setInTerminal(true); logger.setInTerminal(true);
config = loadConfig(configFilepath);
logger.info("Reload config successfully!");
} }
} }
} }

View File

@@ -22,3 +22,33 @@ export function concatSentence(words: string[], length: number): string[] {
return ret; return ret;
} }
/**
* Deep copy function for TypeScript.
* @param T Generic type of target/copied value.
* @param target Target value to be copied.
* @see Source project, ts-deepcopy https://github.com/ykdr2017/ts-deepcopy
* @see Code pen https://codepen.io/erikvullings/pen/ejyBYg
*/
export const deepCopy = <T>(target: T): T => {
if (target === null) {
return target;
}
if (target instanceof Date) {
return new Date(target.getTime()) as any;
}
if (target instanceof Array) {
const cp = [] as any[];
(target as any[]).forEach((v) => { cp.push(v); });
return cp.map((n: any) => deepCopy<any>(n)) as any;
}
if (typeof target === 'object') {
const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any };
Object.keys(cp).forEach(k => {
cp[k] = deepCopy<any>(cp[k]);
});
return cp as T;
}
return target;
};
@MohammadFakhreddin