diff --git a/src/accesscontrol/main.ts b/src/accesscontrol/main.ts index a70f3bb..1a1acea 100644 --- a/src/accesscontrol/main.ts +++ b/src/accesscontrol/main.ts @@ -1,4 +1,3 @@ -import { CCLog, DAY, LogLevel } from "@/lib/ccLog"; import { ToastConfig, UserGroupConfig, loadConfig } from "./config"; import { createAccessControlCli } from "./cli"; import { launchAccessControlTUI } from "./tui"; @@ -7,14 +6,31 @@ import { ReadWriteLock } from "@/lib/mutex/ReadWriteLock"; import { ChatManager } from "@/lib/ChatManager"; import { gTimerManager } from "@/lib/TimerManager"; import { KeyEvent, pullEventAs } from "@/lib/event"; +import { + ConditionalStream, + ConsoleStream, + DAY, + FileStream, + Logger, + LogLevel, + processor, + textRenderer, +} from "@/lib/ccStructLog"; const args = [...$vararg]; // Init Log -const logger = new CCLog("accesscontrol.log", { - printTerminal: true, - logInterval: DAY, - outputMinLevel: LogLevel.Info, +let isOnConsoleStream = true; +const logger = new Logger({ + processors: [ + processor.filterByLevel(LogLevel.Info), + processor.addTimestamp(), + ], + renderer: textRenderer, + streams: [ + new ConditionalStream(new ConsoleStream(), () => isOnConsoleStream), + new FileStream("accesscontrol.log", DAY), + ], }); // Load Config @@ -26,7 +42,7 @@ logger.debug(textutils.serialise(config, { allow_repetitions: true })); // Peripheral const playerDetector = peripheral.find( - "playerDetector", + "playerDetector", )[0] as PlayerDetectorPeripheral; const chatBox = peripheral.find("chatBox")[0] as ChatBoxPeripheral; const chatManager: ChatManager = new ChatManager([chatBox]); @@ -37,410 +53,419 @@ let gWatchPlayersInfo: { name: string; hasNoticeTimes: number }[] = []; let gIsRunning = true; interface ParseParams { - playerName?: string; - groupName?: string; - info?: PlayerInfo; + playerName?: string; + groupName?: string; + info?: PlayerInfo; } function reloadConfig() { - let releaser = configLock.tryAcquireWrite(); - while (releaser === undefined) { - sleep(1); - releaser = configLock.tryAcquireWrite(); - } + let releaser = configLock.tryAcquireWrite(); + while (releaser === undefined) { + sleep(1); + releaser = configLock.tryAcquireWrite(); + } - config = loadConfig(configFilepath)!; - gInRangePlayers = []; - gWatchPlayersInfo = []; - releaser.release(); - logger.info("Reload config successfully!"); + config = loadConfig(configFilepath)!; + gInRangePlayers = []; + gWatchPlayersInfo = []; + releaser.release(); + logger.info("Reload config successfully!"); } function safeParseTextComponent( - component: MinecraftTextComponent, - params?: ParseParams, + component: MinecraftTextComponent, + params?: ParseParams, ): MinecraftTextComponent { - const newComponent = deepCopy(component); + const newComponent = deepCopy(component); - if (newComponent.text == undefined) { - newComponent.text = "Wrong text, please contanct with admin"; - } else if (newComponent.text.includes("%")) { - newComponent.text = newComponent.text.replace( - "%playerName%", - params?.playerName ?? "UnknowPlayer", - ); - newComponent.text = newComponent.text.replace( - "%groupName%", - params?.groupName ?? "UnknowGroup", - ); - newComponent.text = newComponent.text.replace( - "%playerPosX%", - params?.info?.x.toString() ?? "UnknowPosX", - ); - newComponent.text = newComponent.text.replace( - "%playerPosY%", - params?.info?.y.toString() ?? "UnknowPosY", - ); - newComponent.text = newComponent.text.replace( - "%playerPosZ%", - params?.info?.z.toString() ?? "UnknowPosZ", - ); - } - return newComponent; + if (newComponent.text == undefined) { + newComponent.text = "Wrong text, please contanct with admin"; + } else if (newComponent.text.includes("%")) { + newComponent.text = newComponent.text.replace( + "%playerName%", + params?.playerName ?? "UnknowPlayer", + ); + newComponent.text = newComponent.text.replace( + "%groupName%", + params?.groupName ?? "UnknowGroup", + ); + newComponent.text = newComponent.text.replace( + "%playerPosX%", + params?.info?.x.toString() ?? "UnknowPosX", + ); + newComponent.text = newComponent.text.replace( + "%playerPosY%", + params?.info?.y.toString() ?? "UnknowPosY", + ); + newComponent.text = newComponent.text.replace( + "%playerPosZ%", + params?.info?.z.toString() ?? "UnknowPosZ", + ); + } + return newComponent; } function sendMessage( - toastConfig: ToastConfig, - targetPlayer: string, - params: ParseParams, + toastConfig: ToastConfig, + targetPlayer: string, + params: ParseParams, ) { - let releaser = configLock.tryAcquireRead(); - while (releaser === undefined) { - sleep(0.1); - releaser = configLock.tryAcquireRead(); - } + 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, - }); + 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(); + releaser.release(); } function sendToast( - toastConfig: ToastConfig, - targetPlayer: string, - params: ParseParams, + toastConfig: ToastConfig, + targetPlayer: string, + params: ParseParams, ) { - let releaser = configLock.tryAcquireRead(); - while (releaser === undefined) { - sleep(0.1); - releaser = configLock.tryAcquireRead(); - } + let releaser = configLock.tryAcquireRead(); + while (releaser === undefined) { + sleep(0.1); + releaser = configLock.tryAcquireRead(); + } - chatManager.sendToast({ - message: safeParseTextComponent( - toastConfig.msg ?? config.welcomeToastConfig.msg, - params, - ), - title: safeParseTextComponent( - toastConfig.title ?? config.welcomeToastConfig.title, - 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(); + chatManager.sendToast({ + message: safeParseTextComponent( + toastConfig.msg ?? config.welcomeToastConfig.msg, + params, + ), + title: safeParseTextComponent( + toastConfig.title ?? config.welcomeToastConfig.title, + 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 sendNotice(player: string, playerInfo?: PlayerInfo) { - let releaser = configLock.tryAcquireRead(); - while (releaser === undefined) { - sleep(0.1); - releaser = configLock.tryAcquireRead(); - } + let releaser = configLock.tryAcquireRead(); + while (releaser === undefined) { + sleep(0.1); + releaser = configLock.tryAcquireRead(); + } - const onlinePlayers = playerDetector.getOnlinePlayers(); - const noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat( - config.usersGroups - .filter((value) => value.isNotice) - .flatMap((value) => value.groupUsers ?? []), - ); - logger.debug(`noticeTargetPlayers: ${noticeTargetPlayers.join(", ")}`); + const onlinePlayers = playerDetector.getOnlinePlayers(); + const noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat( + config.usersGroups + .filter((value) => value.isNotice) + .flatMap((value) => value.groupUsers ?? []), + ); + logger.debug(`noticeTargetPlayers: ${noticeTargetPlayers.join(", ")}`); - for (const targetPlayer of noticeTargetPlayers) { - if (!onlinePlayers.includes(targetPlayer)) continue; - sendToast(config.noticeToastConfig, targetPlayer, { - playerName: player, - info: playerInfo, - }); - sleep(1); - } - releaser.release(); + for (const targetPlayer of noticeTargetPlayers) { + if (!onlinePlayers.includes(targetPlayer)) continue; + sendToast(config.noticeToastConfig, targetPlayer, { + playerName: player, + info: playerInfo, + }); + sleep(1); + } + 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, { playerName: player }); - chatManager.sendMessage({ - message: safeParseTextComponent(config.warnToastConfig.msg, { - playerName: player, - }), - targetPlayer: player, - prefix: "AccessControl", - brackets: "[]", - utf8Support: true, - }); - releaser.release(); -} - -function watchLoop() { - while (gIsRunning) { - const releaser = configLock.tryAcquireRead(); - if (releaser === undefined) { - os.sleep(1); - continue; - } - - const watchPlayerNames = gWatchPlayersInfo.flatMap((value) => value.name); - logger.debug(`Watch Players [ ${watchPlayerNames.join(", ")} ]`); - for (const player of gWatchPlayersInfo) { - const playerInfo = playerDetector.getPlayerPos(player.name); - if (gInRangePlayers.includes(player.name)) { - // Notice - if (player.hasNoticeTimes < config.noticeTimes) { - sendNotice(player.name, playerInfo); - player.hasNoticeTimes += 1; - } - - // Warn - if (config.isWarn) sendWarn(player.name); - - // Record - logger.warn( - `Stranger ${player.name} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, - ); - } else { - // Get rid of player from list - gWatchPlayersInfo = gWatchPlayersInfo.filter( - (value) => value.name != player.name, - ); - logger.info( - `Stranger ${player.name} has left the range at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, - ); - } - os.sleep(1); - } - - releaser.release(); - os.sleep(config.watchInterval); - } -} - -function mainLoop() { - while (gIsRunning) { - 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}`); - - for (const player of players) { - if (gInRangePlayers.includes(player)) continue; - - // Get player Info - const playerInfo = playerDetector.getPlayerPos(player); - - if (config.adminGroupConfig.groupUsers.includes(player)) { - 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 - let groupConfig: UserGroupConfig = { - groupName: "Unfamiliar", - groupUsers: [], - isAllowed: false, - isNotice: false, - isWelcome: false, - }; - - // Get user group config - for (const userGroupConfig of config.usersGroups) { - if (userGroupConfig.groupUsers == undefined) continue; - if (!userGroupConfig.groupUsers.includes(player)) continue; - - groupConfig = userGroupConfig; - logger.info( - `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, - ); - if (userGroupConfig.isWelcome) - sendMessage(config.welcomeToastConfig, player, { - playerName: player, - groupName: groupConfig.groupName, - info: playerInfo, - }); - - break; - } - - if (groupConfig.isAllowed) continue; - - logger.warn( - `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, - ); - if (config.isWelcome) - sendMessage(config.welcomeToastConfig, player, { - playerName: player, - groupName: groupConfig.groupName, - info: playerInfo, - }); - if (config.isWarn) sendWarn(player); - gWatchPlayersInfo = [ - ...gWatchPlayersInfo, - { name: player, hasNoticeTimes: 0 }, - ]; - } - - gInRangePlayers = players; - releaser.release(); - os.sleep(config.detectInterval); - } -} - -function keyboardLoop() { - 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); - launchAccessControlTUI(); - logger.info("TUI closed, resuming normal operation"); - } catch (error) { - logger.error(`TUI error: ${textutils.serialise(error as object)}`); - } finally { - logger.setInTerminal(true); - reloadConfig(); - } - } else if (event.key === keys.r) { - reloadConfig(); - } - // else if (event.key === keys.q) { - // gIsRunning = false; - // } - } -} - -function cliLoop() { - let printTargetPlayer: string | undefined; - const cli = createAccessControlCli({ - configFilepath: configFilepath, - reloadConfig: () => reloadConfig(), - logger: logger, - print: (msg) => - chatManager.sendMessage({ - message: msg, - targetPlayer: printTargetPlayer, - prefix: "Access Control System", - brackets: "[]", - utf8Support: true, - }), - }); - - while (gIsRunning) { - const result = chatManager.getReceivedMessage(); - if (result.isErr()) { - sleep(0.5); - continue; - } - logger.debug(`Received message: ${result.value.message}`); - - const ev = result.value; + 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(); + sleep(0.1); + releaser = configLock.tryAcquireRead(); } - const isAdmin = config.adminGroupConfig.groupUsers.includes(ev.username); - + sendToast(config.warnToastConfig, player, { playerName: player }); + chatManager.sendMessage({ + message: safeParseTextComponent(config.warnToastConfig.msg, { + playerName: player, + }), + targetPlayer: player, + prefix: "AccessControl", + brackets: "[]", + utf8Support: true, + }); releaser.release(); - if (!isAdmin) continue; - if (!ev.message.startsWith("@AC")) continue; +} - printTargetPlayer = ev.username; - logger.info( - `Received command "${ev.message}" from admin ${printTargetPlayer}`, - ); +function watchLoop() { + while (gIsRunning) { + const releaser = configLock.tryAcquireRead(); + if (releaser === undefined) { + os.sleep(1); + continue; + } - const commandArgs = ev.message - .substring(3) - .split(" ") - .filter((s) => s.length > 0); - logger.debug(`Command arguments: ${commandArgs.join(", ")}`); + const watchPlayerNames = gWatchPlayersInfo.flatMap( + (value) => value.name, + ); + logger.debug(`Watch Players [ ${watchPlayerNames.join(", ")} ]`); + for (const player of gWatchPlayersInfo) { + const playerInfo = playerDetector.getPlayerPos(player.name); + if (gInRangePlayers.includes(player.name)) { + // Notice + if (player.hasNoticeTimes < config.noticeTimes) { + sendNotice(player.name, playerInfo); + player.hasNoticeTimes += 1; + } - cli(commandArgs); - printTargetPlayer = undefined; - } + // Warn + if (config.isWarn) sendWarn(player.name); + + // Record + logger.warn( + `Stranger ${player.name} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, + ); + } else { + // Get rid of player from list + gWatchPlayersInfo = gWatchPlayersInfo.filter( + (value) => value.name != player.name, + ); + logger.info( + `Stranger ${player.name} has left the range at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, + ); + } + os.sleep(1); + } + + releaser.release(); + os.sleep(config.watchInterval); + } +} + +function mainLoop() { + while (gIsRunning) { + 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}`); + + for (const player of players) { + if (gInRangePlayers.includes(player)) continue; + + // Get player Info + const playerInfo = playerDetector.getPlayerPos(player); + + if (config.adminGroupConfig.groupUsers.includes(player)) { + 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 + let groupConfig: UserGroupConfig = { + groupName: "Unfamiliar", + groupUsers: [], + isAllowed: false, + isNotice: false, + isWelcome: false, + }; + + // Get user group config + for (const userGroupConfig of config.usersGroups) { + if (userGroupConfig.groupUsers == undefined) continue; + if (!userGroupConfig.groupUsers.includes(player)) continue; + + groupConfig = userGroupConfig; + logger.info( + `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, + ); + if (userGroupConfig.isWelcome) + sendMessage(config.welcomeToastConfig, player, { + playerName: player, + groupName: groupConfig.groupName, + info: playerInfo, + }); + + break; + } + + if (groupConfig.isAllowed) continue; + + logger.warn( + `${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`, + ); + if (config.isWelcome) + sendMessage(config.welcomeToastConfig, player, { + playerName: player, + groupName: groupConfig.groupName, + info: playerInfo, + }); + if (config.isWarn) sendWarn(player); + gWatchPlayersInfo = [ + ...gWatchPlayersInfo, + { name: player, hasNoticeTimes: 0 }, + ]; + } + + gInRangePlayers = players; + releaser.release(); + os.sleep(config.detectInterval); + } +} + +function keyboardLoop() { + while (gIsRunning) { + const event = pullEventAs(KeyEvent, "key"); + if (event === undefined) continue; + + if (event.key === keys.c) { + logger.info("Launching Access Control TUI..."); + try { + isOnConsoleStream = false; + launchAccessControlTUI(); + logger.info("TUI closed, resuming normal operation"); + } catch (error) { + logger.error( + `TUI error: ${textutils.serialise(error as object)}`, + ); + } finally { + isOnConsoleStream = true; + reloadConfig(); + } + } else if (event.key === keys.r) { + reloadConfig(); + } + // else if (event.key === keys.q) { + // gIsRunning = false; + // } + } +} + +function cliLoop() { + let printTargetPlayer: string | undefined; + const cli = createAccessControlCli({ + configFilepath: configFilepath, + reloadConfig: () => reloadConfig(), + logger: logger, + print: (msg) => + chatManager.sendMessage({ + message: msg, + targetPlayer: printTargetPlayer, + prefix: "Access Control System", + brackets: "[]", + utf8Support: true, + }), + }); + + while (gIsRunning) { + const result = chatManager.getReceivedMessage(); + if (result.isErr()) { + sleep(0.5); + continue; + } + logger.debug(`Received message: ${result.value.message}`); + + const ev = result.value; + + let releaser = configLock.tryAcquireRead(); + while (releaser === undefined) { + sleep(0.1); + releaser = configLock.tryAcquireRead(); + } + + const isAdmin = config.adminGroupConfig.groupUsers.includes( + ev.username, + ); + + releaser.release(); + if (!isAdmin) continue; + if (!ev.message.startsWith("@AC")) continue; + + printTargetPlayer = ev.username; + logger.info( + `Received command "${ev.message}" from admin ${printTargetPlayer}`, + ); + + const commandArgs = ev.message + .substring(3) + .split(" ") + .filter((s) => s.length > 0); + logger.debug(`Command arguments: ${commandArgs.join(", ")}`); + + cli(commandArgs); + printTargetPlayer = undefined; + } } function main(args: string[]) { - logger.info("Starting access control system, get args: " + args.join(", ")); - if (args.length == 1) { - if (args[0] == "start") { - 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(), - () => cliLoop(), - () => watchLoop(), - () => keyboardLoop(), - () => chatManager.run(), - ); + logger.info("Starting access control system, get args: " + args.join(", ")); + if (args.length == 1) { + if (args[0] == "start") { + 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(), + () => cliLoop(), + () => watchLoop(), + () => keyboardLoop(), + () => chatManager.run(), + ); - return; - } else if (args[0] == "config") { - logger.info("Launching Access Control TUI..."); - logger.setInTerminal(false); - try { - launchAccessControlTUI(); - } catch (error) { - logger.error(`TUI error: ${textutils.serialise(error as object)}`); - } - return; + return; + } else if (args[0] == "config") { + logger.info("Launching Access Control TUI..."); + isOnConsoleStream = false; + + try { + launchAccessControlTUI(); + } catch (error) { + logger.error( + `TUI error: ${textutils.serialise(error as object)}`, + ); + } + return; + } } - } - print(`Usage: accesscontrol start | config`); - print(" start - Start the access control system with monitoring"); - print(" config - Open configuration TUI"); + print(`Usage: accesscontrol start | config`); + print(" start - Start the access control system with monitoring"); + print(" config - Open configuration TUI"); } try { - main(args); + main(args); } catch (error: unknown) { - logger.error(textutils.serialise(error as object)); + logger.error(textutils.serialise(error as object)); } finally { - logger.close(); + logger.close(); } diff --git a/src/autocraft/main.ts b/src/autocraft/main.ts index 9c36f52..e797b97 100644 --- a/src/autocraft/main.ts +++ b/src/autocraft/main.ts @@ -1,215 +1,242 @@ import { - CraftManager, - CraftRecipe, - CreatePackageTag, + CraftManager, + CraftRecipe, + CreatePackageTag, } from "@/lib/CraftManager"; -import { CCLog, LogLevel } from "@/lib/ccLog"; import { Queue } from "@/lib/datatype/Queue"; +import { + ConsoleStream, + Logger, + LogLevel, + processor, + textRenderer, +} from "@/lib/ccStructLog"; -const logger = new CCLog("autocraft.log", { outputMinLevel: LogLevel.Info }); +const logger = new Logger({ + processors: [ + processor.filterByLevel(LogLevel.Info), + processor.addFullTimestamp(), + ], + renderer: textRenderer, + streams: [new ConsoleStream()], +}); const peripheralsNames = { - // packsInventory: "minecraft:chest_14", - // itemsInventory: "minecraft:chest_15", - // packageExtractor: "create:packager_3", - blockReader: "bottom", - wiredModem: "right", - redstone: "left", - packsInventory: "minecraft:chest_1121", - itemsInventory: "minecraft:chest_1120", - packageExtractor: "create:packager_0", + // packsInventory: "minecraft:chest_14", + // itemsInventory: "minecraft:chest_15", + // packageExtractor: "create:packager_3", + blockReader: "bottom", + wiredModem: "right", + redstone: "left", + packsInventory: "minecraft:chest_1121", + itemsInventory: "minecraft:chest_1120", + packageExtractor: "create:packager_0", }; const packsInventory = peripheral.wrap( - peripheralsNames.packsInventory, + peripheralsNames.packsInventory, ) as InventoryPeripheral; const itemsInventory = peripheral.wrap( - peripheralsNames.itemsInventory, + peripheralsNames.itemsInventory, ) as InventoryPeripheral; const packageExtractor = peripheral.wrap( - peripheralsNames.packageExtractor, + peripheralsNames.packageExtractor, ) as InventoryPeripheral; const blockReader = peripheral.wrap( - peripheralsNames.blockReader, + peripheralsNames.blockReader, ) as BlockReaderPeripheral; const wiredModem = peripheral.wrap( - peripheralsNames.wiredModem, + peripheralsNames.wiredModem, ) as WiredModemPeripheral; const turtleLocalName = wiredModem.getNameLocal(); enum State { - IDLE, - READ_RECIPE, - CRAFT_OUTPUT, + IDLE, + READ_RECIPE, + CRAFT_OUTPUT, } function main() { - const craftManager = new CraftManager(turtleLocalName, itemsInventory); - const recipesQueue = new Queue(); - const recipesWaitingMap = new Map(); - let currentState = State.IDLE; - let nextState = State.IDLE; - let hasPackage = redstone.getInput(peripheralsNames.redstone); - while (hasPackage) { - hasPackage = redstone.getInput(peripheralsNames.redstone); - logger.warn("redstone activated when init, please clear inventory"); - sleep(1); - } - - logger.info("AutoCraft init finished..."); - while (true) { - // Switch state - switch (currentState) { - case State.IDLE: { - nextState = hasPackage ? State.READ_RECIPE : State.IDLE; - break; - } - case State.READ_RECIPE: { - nextState = hasPackage ? State.READ_RECIPE : State.CRAFT_OUTPUT; - break; - } - case State.CRAFT_OUTPUT: { - nextState = - recipesQueue.size() > 0 - ? State.CRAFT_OUTPUT - : hasPackage - ? State.READ_RECIPE - : State.IDLE; - break; - } - default: { - logger.error(`Unknown state`); - nextState = hasPackage ? State.READ_RECIPE : State.IDLE; - break; - } - } - - // State logic - switch (currentState) { - case State.IDLE: { - if (!hasPackage) os.pullEvent("redstone"); + const craftManager = new CraftManager(turtleLocalName, itemsInventory); + const recipesQueue = new Queue(); + const recipesWaitingMap = new Map(); + let currentState = State.IDLE; + let nextState = State.IDLE; + let hasPackage = redstone.getInput(peripheralsNames.redstone); + while (hasPackage) { hasPackage = redstone.getInput(peripheralsNames.redstone); - break; - } - - case State.READ_RECIPE: { - logger.info(`Package detected`); - const packagesInfoRecord = packsInventory.list(); - for (const key in packagesInfoRecord) { - const slotNum = parseInt(key); - packsInventory.pushItems(turtleLocalName, slotNum); - - // Get package NBT - logger.debug( - `Turtle:\n${textutils.serialise(blockReader.getBlockData()!, { allow_repetitions: true })}`, - ); - const packageDetailInfo = blockReader.getBlockData()?.Items[1]; - if (packageDetailInfo === undefined) { - logger.error(`Package detail info not found`); - continue; - } - - // Get OrderId and isFinal - const packageOrderId = (packageDetailInfo.tag as CreatePackageTag) - .Fragment.OrderId; - const packageIsFinal = - (packageDetailInfo.tag as CreatePackageTag).Fragment.IsFinal > 0 - ? true - : false; - - // Get recipe - const packageRecipes = - CraftManager.getPackageRecipe(packageDetailInfo); - if (packageRecipes.isSome()) { - if (packageIsFinal) recipesQueue.enqueue(packageRecipes.value); - else recipesWaitingMap.set(packageOrderId, packageRecipes.value); - } else { - if (packageIsFinal && recipesWaitingMap.has(packageOrderId)) { - recipesQueue.enqueue(recipesWaitingMap.get(packageOrderId)!); - recipesWaitingMap.delete(packageOrderId); - } else { - logger.debug(`No recipe, just pass`); - } - } - packageExtractor.pullItems(turtleLocalName, 1); - } - - if ( - currentState === State.READ_RECIPE && - nextState === State.CRAFT_OUTPUT - ) { - craftManager.initItemsMap(); - } - - break; - } - - case State.CRAFT_OUTPUT: { - // Check recipe - const recipe = recipesQueue.dequeue(); - if (recipe === undefined) break; - - let restCraftCnt = recipe.Count; - let maxSignleCraftCnt = restCraftCnt; - - let craftItemDetail: ItemDetail | undefined = undefined; - do { - // Clear workbench - craftManager.clearTurtle(); - - logger.info(`Pull items according to a recipe`); - const craftCnt = craftManager - .pullItemsWithRecipe(recipe, maxSignleCraftCnt) - .unwrapOrElse((error) => { - logger.error(error.message); - return 0; - }); - - if (craftCnt == 0) break; - if (craftCnt < maxSignleCraftCnt) maxSignleCraftCnt = craftCnt; - const craftRet = craftManager.craft(maxSignleCraftCnt); - craftItemDetail ??= craftRet; - logger.info(`Craft ${craftCnt} times`); - restCraftCnt -= craftCnt; - } while (restCraftCnt > 0); - - // Finally output - if (restCraftCnt > 0) { - logger.warn( - `Only craft ${recipe.Count - restCraftCnt}x ${craftItemDetail?.name ?? "UnknownItem"}`, - ); - } else { - logger.info( - `Finish craft ${recipe.Count}x ${craftItemDetail?.name ?? "UnknownItem"}`, - ); - } - - // Clear workbench and inventory - const turtleItemSlots = Object.values( - blockReader.getBlockData()!.Items, - ).map((val) => val.Slot + 1); - craftManager.clearTurtle(turtleItemSlots); - - break; - } - - default: { + logger.warn("redstone activated when init, please clear inventory"); sleep(1); - break; - } } - // Check packages - hasPackage = redstone.getInput(peripheralsNames.redstone); - // State update - currentState = nextState; - } + logger.info("AutoCraft init finished..."); + while (true) { + // Switch state + switch (currentState) { + case State.IDLE: { + nextState = hasPackage ? State.READ_RECIPE : State.IDLE; + break; + } + case State.READ_RECIPE: { + nextState = hasPackage ? State.READ_RECIPE : State.CRAFT_OUTPUT; + break; + } + case State.CRAFT_OUTPUT: { + nextState = + recipesQueue.size() > 0 + ? State.CRAFT_OUTPUT + : hasPackage + ? State.READ_RECIPE + : State.IDLE; + break; + } + default: { + logger.error(`Unknown state`); + nextState = hasPackage ? State.READ_RECIPE : State.IDLE; + break; + } + } + + // State logic + switch (currentState) { + case State.IDLE: { + if (!hasPackage) os.pullEvent("redstone"); + hasPackage = redstone.getInput(peripheralsNames.redstone); + break; + } + + case State.READ_RECIPE: { + logger.info(`Package detected`); + const packagesInfoRecord = packsInventory.list(); + for (const key in packagesInfoRecord) { + const slotNum = parseInt(key); + packsInventory.pushItems(turtleLocalName, slotNum); + + // Get package NBT + logger.debug( + `Turtle:\n${textutils.serialise(blockReader.getBlockData()!, { allow_repetitions: true })}`, + ); + const packageDetailInfo = + blockReader.getBlockData()?.Items[1]; + if (packageDetailInfo === undefined) { + logger.error(`Package detail info not found`); + continue; + } + + // Get OrderId and isFinal + const packageOrderId = ( + packageDetailInfo.tag as CreatePackageTag + ).Fragment.OrderId; + const packageIsFinal = + (packageDetailInfo.tag as CreatePackageTag).Fragment + .IsFinal > 0 + ? true + : false; + + // Get recipe + const packageRecipes = + CraftManager.getPackageRecipe(packageDetailInfo); + if (packageRecipes.isSome()) { + if (packageIsFinal) + recipesQueue.enqueue(packageRecipes.value); + else + recipesWaitingMap.set( + packageOrderId, + packageRecipes.value, + ); + } else { + if ( + packageIsFinal && + recipesWaitingMap.has(packageOrderId) + ) { + recipesQueue.enqueue( + recipesWaitingMap.get(packageOrderId)!, + ); + recipesWaitingMap.delete(packageOrderId); + } else { + logger.debug(`No recipe, just pass`); + } + } + packageExtractor.pullItems(turtleLocalName, 1); + } + + if ( + currentState === State.READ_RECIPE && + nextState === State.CRAFT_OUTPUT + ) { + craftManager.initItemsMap(); + } + + break; + } + + case State.CRAFT_OUTPUT: { + // Check recipe + const recipe = recipesQueue.dequeue(); + if (recipe === undefined) break; + + let restCraftCnt = recipe.Count; + let maxSignleCraftCnt = restCraftCnt; + + let craftItemDetail: ItemDetail | undefined = undefined; + do { + // Clear workbench + craftManager.clearTurtle(); + + logger.info(`Pull items according to a recipe`); + const craftCnt = craftManager + .pullItemsWithRecipe(recipe, maxSignleCraftCnt) + .unwrapOrElse((error) => { + logger.error(error.message); + return 0; + }); + + if (craftCnt == 0) break; + if (craftCnt < maxSignleCraftCnt) + maxSignleCraftCnt = craftCnt; + const craftRet = craftManager.craft(maxSignleCraftCnt); + craftItemDetail ??= craftRet; + logger.info(`Craft ${craftCnt} times`); + restCraftCnt -= craftCnt; + } while (restCraftCnt > 0); + + // Finally output + if (restCraftCnt > 0) { + logger.warn( + `Only craft ${recipe.Count - restCraftCnt}x ${craftItemDetail?.name ?? "UnknownItem"}`, + ); + } else { + logger.info( + `Finish craft ${recipe.Count}x ${craftItemDetail?.name ?? "UnknownItem"}`, + ); + } + + // Clear workbench and inventory + const turtleItemSlots = Object.values( + blockReader.getBlockData()!.Items, + ).map((val) => val.Slot + 1); + craftManager.clearTurtle(turtleItemSlots); + + break; + } + + default: { + sleep(1); + break; + } + } + + // Check packages + hasPackage = redstone.getInput(peripheralsNames.redstone); + // State update + currentState = nextState; + } } try { - main(); + main(); } catch (error: unknown) { - logger.error(textutils.serialise(error as object)); + logger.error(textutils.serialise(error as object)); } finally { - logger.close(); + logger.close(); } diff --git a/src/logExample/main.ts b/src/logExample/main.ts index 8377849..512dd20 100644 --- a/src/logExample/main.ts +++ b/src/logExample/main.ts @@ -7,56 +7,16 @@ import { Logger, - createDevLogger, - createProdLogger, - - // Processors - addTimestamp, - addFormattedTimestamp, - addFullTimestamp, - addSource, - addComputerId, - addStaticFields, - transformField, - - // Renderers - textRenderer, - jsonRenderer, - - // Streams + processor, ConsoleStream, FileStream, BufferStream, - DAY, + ConditionalStream, HOUR, + jsonRenderer, LogLevel, + textRenderer, } from "../lib/ccStructLog"; -import { ConditionalStream } from "@/lib/ccStructLog/streams"; - -// ============================================================================= -// Basic Usage Examples -// ============================================================================= - -print("=== Basic Usage Examples ==="); - -// 1. Quick start with pre-configured loggers -const devLog = createDevLogger(); -devLog.info("Application started", { version: "1.0.0", port: 8080 }); -devLog.debug("Debug information", { userId: 123, action: "login" }); -devLog.error("Something went wrong", { - error: "Connection failed", - retries: 3, -}); - -// 2. Production logging to file -const prodLog = createProdLogger("app.log", { - source: "MyApplication", - rotationInterval: DAY, - includeConsole: true, -}); - -prodLog.info("User action", { userId: 456, action: "purchase", amount: 29.99 }); -prodLog.warn("Low disk space", { available: 1024, threshold: 2048 }); // ============================================================================= // Custom Logger Configurations @@ -67,10 +27,10 @@ print("\n=== Custom Logger Configurations ==="); // 4. Custom logger with specific processors and renderer const customLogger = new Logger({ processors: [ - addFullTimestamp(), - addComputerId(), - addSource("CustomApp"), - addStaticFields({ + processor.addTimestamp(), + processor.addComputerId(), + processor.addSource("CustomApp"), + processor.addStaticFields({ environment: "development", version: "2.1.0", }), @@ -109,10 +69,10 @@ const sanitizePasswords = (event: Map) => { const secureLogger = new Logger({ processors: [ - addTimestamp(), + processor.addTimestamp(), addRequestId, sanitizePasswords, - transformField("message", (msg) => `[SECURE] ${msg}`), + processor.transformField("message", (msg) => `[SECURE] ${msg}`), ], renderer: jsonRenderer, streams: [new ConsoleStream()], @@ -133,7 +93,7 @@ print("\n=== Stream Examples ==="); // 11. Buffer stream for batch processing const bufferStream = new BufferStream(100); // Keep last 100 messages const bufferLogger = new Logger({ - processors: [addFormattedTimestamp()], + processors: [processor.addTimestamp()], renderer: textRenderer, streams: [ new ConditionalStream(new ConsoleStream(), (msg, event) => { @@ -162,7 +122,7 @@ for (const msg of bufferedMessages) { // 12. Multi-stream with different formats const multiFormatLogger = new Logger({ - processors: [addFullTimestamp(), addComputerId()], + processors: [processor.addTimestamp(), processor.addComputerId()], renderer: (event) => "default", // This won't be used streams: [ // Console with human-readable format @@ -196,7 +156,7 @@ print("\n=== Error Handling Examples ==="); // 13. Robust error handling const robustLogger = new Logger({ processors: [ - addTimestamp(), + processor.addTimestamp(), // Processor that might fail (event) => { try { @@ -231,7 +191,7 @@ print("\n=== Cleanup Examples ==="); // 14. Proper cleanup const fileLogger = new Logger({ - processors: [addTimestamp()], + processors: [processor.addTimestamp()], renderer: jsonRenderer, streams: [new FileStream("temp.log")], }); @@ -249,37 +209,3 @@ print("- all.log (complete log)"); print("- debug.log (detailed debug info)"); print("- structured.log (JSON format)"); print("- temp.log (temporary file, now closed)"); - -// ============================================================================= -// Performance Comparison (commented out to avoid noise) -// ============================================================================= - -/* -print("\n=== Performance Comparison ==="); - -const iterations = 1000; - -// Test simple console logging -const startTime1 = os.clock(); -const simpleLogger = createMinimalLogger(); -for (let i = 0; i < iterations; i++) { - simpleLogger.info(`Simple message ${i}`); -} -const endTime1 = os.clock(); -print(`Simple Console Logger: ${endTime1 - startTime1} seconds`); - -// Test complex processor chain -const startTime2 = os.clock(); -const complexLogger = createDetailedLogger("perf_test.log", { - source: "PerfTest" -}); -for (let i = 0; i < iterations; i++) { - complexLogger.info(`Complex message ${i}`, { - iteration: i, - data: { nested: { value: i * 2 } } - }); -} -complexLogger.close(); -const endTime2 = os.clock(); -print(`Complex Processor Chain: ${endTime2 - startTime2} seconds`); -*/