mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-11-05 03:37:50 +08:00
finish basic access control system
This commit is contained in:
67
src/accesscontrol/access.config.json
Normal file
67
src/accesscontrol/access.config.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"detectRange": 64,
|
||||
"detectInterval": 1,
|
||||
"warnInterval": 7,
|
||||
"adminGroupConfig": {
|
||||
"groupName": "Admin",
|
||||
"groupUsers": ["Selcon"],
|
||||
"isAllowed": true,
|
||||
"isWarnTarget": true
|
||||
},
|
||||
"defaultToastConfig": {
|
||||
"title": {
|
||||
"text": "Welcome",
|
||||
"color": "green"
|
||||
},
|
||||
"msg": {
|
||||
"text": "Hello %groupName% %playerName%",
|
||||
"color": "green"
|
||||
},
|
||||
"prefix": "桃花源",
|
||||
"brackets": "[]",
|
||||
"bracketColor": ""
|
||||
},
|
||||
"warnToastConfig": {
|
||||
"title": {
|
||||
"text": "Attention!!!",
|
||||
"color": "red"
|
||||
},
|
||||
"msg": {
|
||||
"text": "%playerName% you are not allowed to be here",
|
||||
"color": "red"
|
||||
},
|
||||
"prefix": "Taohuayuan",
|
||||
"brackets": "[]",
|
||||
"bracketColor": ""
|
||||
},
|
||||
"usersGroups": [
|
||||
{
|
||||
"groupName": "user",
|
||||
"groupUsers": [],
|
||||
"isAllowed": true,
|
||||
"isWarnTarget": true
|
||||
},
|
||||
{
|
||||
"groupName": "VIP",
|
||||
"groupUsers": [],
|
||||
"isAllowed": true,
|
||||
"isWarnTarget": false
|
||||
},
|
||||
{
|
||||
"groupName": "enemy",
|
||||
"groupUsers": [],
|
||||
"isAllowed": false,
|
||||
"isWarnTarget": false,
|
||||
"toastConfig": {
|
||||
"title": {
|
||||
"text": "Warn",
|
||||
"color": "red"
|
||||
},
|
||||
"msg": {
|
||||
"text": "Warn %playerName%",
|
||||
"color": "red"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
0
src/accesscontrol/cli.ts
Normal file
0
src/accesscontrol/cli.ts
Normal file
158
src/accesscontrol/config.ts
Normal file
158
src/accesscontrol/config.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { CCLog } from "@/lib/ccLog";
|
||||
import * as dkjson from "@sikongjueluo/dkjson-types";
|
||||
|
||||
let log: CCLog | undefined;
|
||||
|
||||
interface ToastConfig {
|
||||
title: MinecraftTextComponent;
|
||||
msg: MinecraftTextComponent;
|
||||
prefix?: string;
|
||||
brackets?: string;
|
||||
bracketColor?: string;
|
||||
}
|
||||
|
||||
interface UserGroupConfig {
|
||||
groupName: string;
|
||||
isAllowed: boolean;
|
||||
isWarnTarget: boolean;
|
||||
groupUsers: string[];
|
||||
toastConfig?: ToastConfig;
|
||||
}
|
||||
|
||||
interface AccessConfig {
|
||||
detectInterval: number;
|
||||
warnInterval: number;
|
||||
detectRange: number;
|
||||
adminGroupConfig: UserGroupConfig;
|
||||
defaultToastConfig: ToastConfig;
|
||||
warnToastConfig: ToastConfig;
|
||||
usersGroups: UserGroupConfig[];
|
||||
}
|
||||
|
||||
const defaultConfig: AccessConfig = {
|
||||
detectRange: 64,
|
||||
detectInterval: 3,
|
||||
warnInterval: 7,
|
||||
adminGroupConfig: {
|
||||
groupName: "Admin",
|
||||
groupUsers: ["Selcon"],
|
||||
isAllowed: true,
|
||||
isWarnTarget: false,
|
||||
},
|
||||
usersGroups: [
|
||||
{
|
||||
groupName: "user",
|
||||
groupUsers: [],
|
||||
isAllowed: true,
|
||||
isWarnTarget: true,
|
||||
},
|
||||
{
|
||||
groupName: "VIP",
|
||||
groupUsers: [],
|
||||
isAllowed: true,
|
||||
isWarnTarget: false,
|
||||
},
|
||||
{
|
||||
groupName: "enemies",
|
||||
groupUsers: [],
|
||||
isAllowed: false,
|
||||
isWarnTarget: false,
|
||||
toastConfig: {
|
||||
title: {
|
||||
text: "Warn",
|
||||
color: "red",
|
||||
},
|
||||
msg: {
|
||||
text: "Warn %playerName%",
|
||||
color: "red",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultToastConfig: {
|
||||
title: {
|
||||
text: "Welcome",
|
||||
color: "green",
|
||||
},
|
||||
msg: {
|
||||
text: "Hello User %playerName%",
|
||||
color: "green",
|
||||
},
|
||||
prefix: "Taohuayuan",
|
||||
brackets: "[]",
|
||||
bracketColor: "",
|
||||
},
|
||||
warnToastConfig: {
|
||||
title: {
|
||||
text: "Attention!!!",
|
||||
color: "red",
|
||||
},
|
||||
msg: {
|
||||
text: "%playerName% you are not allowed to be here",
|
||||
color: "red",
|
||||
},
|
||||
prefix: "Taohuayuan",
|
||||
brackets: "[]",
|
||||
bracketColor: "",
|
||||
},
|
||||
};
|
||||
|
||||
function setLog(newLog: CCLog) {
|
||||
log = newLog;
|
||||
}
|
||||
|
||||
function loadConfig(filepath: string): AccessConfig {
|
||||
const [fp] = io.open(filepath, "r");
|
||||
if (fp == undefined) {
|
||||
print("Failed to open config file " + filepath);
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
const configJson = fp.read("*a");
|
||||
if (configJson == undefined) {
|
||||
print("Failed to read config file");
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
const [config, pos, err] = dkjson.decode(configJson);
|
||||
if (config == undefined) {
|
||||
log?.warn(
|
||||
`Config decode failed at ${pos}, use default instead. Error :${err}`,
|
||||
);
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
// Not use external lib
|
||||
// const config = textutils.unserialiseJSON(configJson, {
|
||||
// parse_empty_array: true,
|
||||
// });
|
||||
|
||||
return config as AccessConfig;
|
||||
}
|
||||
|
||||
function saveConfig(config: AccessConfig, filepath: string) {
|
||||
const configJson = dkjson.encode(config, { indent: true }) as string;
|
||||
// Not use external lib
|
||||
// const configJson = textutils.serializeJSON(config, { unicode_strings: true });
|
||||
if (configJson == undefined) {
|
||||
print("Failed to save config");
|
||||
}
|
||||
|
||||
const [fp, _err] = io.open(filepath, "w+");
|
||||
if (fp == undefined) {
|
||||
print("Failed to open config file " + filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
fp.write(configJson);
|
||||
fp.close();
|
||||
}
|
||||
|
||||
export {
|
||||
ToastConfig,
|
||||
UserGroupConfig,
|
||||
AccessConfig,
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
setLog,
|
||||
};
|
||||
355
src/accesscontrol/main.ts
Normal file
355
src/accesscontrol/main.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
import { CCLog, DAY } from "@/lib/ccLog";
|
||||
import {
|
||||
ToastConfig,
|
||||
UserGroupConfig,
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
setLog,
|
||||
} from "./config";
|
||||
import * as peripheralManager from "../lib/PeripheralManager";
|
||||
import { ChatBoxEvent, pullEventAs } from "@/lib/event";
|
||||
import { quotestring } from "@sikongjueluo/dkjson-types";
|
||||
|
||||
const DEBUG = false;
|
||||
const args = [...$vararg];
|
||||
|
||||
const log = new CCLog("accesscontrol.log", DAY);
|
||||
setLog(log);
|
||||
|
||||
const configFilepath = `${shell.dir()}/access.config.json`;
|
||||
const config = loadConfig(configFilepath);
|
||||
log.info("Load config successfully!");
|
||||
log.debug(textutils.serialise(config, { allow_repetitions: true }));
|
||||
const groupNames = config.usersGroups.map((value) => value.groupName);
|
||||
const warnTargetPlayers = config.adminGroupConfig.groupUsers.concat(
|
||||
config.usersGroups
|
||||
.filter((value) => value.isWarnTarget)
|
||||
.map((value) => value.groupUsers ?? [])
|
||||
.flat(),
|
||||
);
|
||||
|
||||
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
|
||||
const chatBox = peripheralManager.findByNameRequired("chatBox");
|
||||
|
||||
let inRangePlayers: string[] = [];
|
||||
let notAllowedPlayers: string[] = [];
|
||||
|
||||
function safeParseTextComponent(
|
||||
component: MinecraftTextComponent,
|
||||
playerName: string,
|
||||
groupName?: string,
|
||||
): string {
|
||||
if (component.text == undefined) {
|
||||
component.text = "Wrong text, please contanct with admin";
|
||||
} else if (component.text.includes("%")) {
|
||||
component.text = component.text.replace("%playerName%", playerName);
|
||||
if (groupName != undefined)
|
||||
component.text = component.text.replace("%groupName%", groupName);
|
||||
}
|
||||
return textutils.serialiseJSON(component);
|
||||
}
|
||||
|
||||
function sendToast(
|
||||
toastConfig: ToastConfig,
|
||||
player: string,
|
||||
groupConfig?: UserGroupConfig,
|
||||
) {
|
||||
return chatBox.sendFormattedToastToPlayer(
|
||||
safeParseTextComponent(
|
||||
toastConfig.msg ?? config.defaultToastConfig.msg,
|
||||
player,
|
||||
groupConfig?.groupName,
|
||||
),
|
||||
safeParseTextComponent(
|
||||
toastConfig.title ?? config.defaultToastConfig.title,
|
||||
player,
|
||||
groupConfig?.groupName,
|
||||
),
|
||||
player,
|
||||
quotestring(toastConfig.prefix ?? config.defaultToastConfig.prefix!),
|
||||
toastConfig.brackets ?? config.defaultToastConfig.brackets,
|
||||
toastConfig.bracketColor ?? config.defaultToastConfig.bracketColor,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
function sendWarn(player: string) {
|
||||
const playerPos = playerDetector.getPlayerPos(player);
|
||||
const onlinePlayers = playerDetector.getOnlinePlayers();
|
||||
|
||||
const warnMsg = `Not Allowed Player ${player} Break in Home at Position ${playerPos?.x}, ${playerPos?.y}, ${playerPos?.z}`;
|
||||
log.warn(warnMsg);
|
||||
sendToast(config.warnToastConfig, player);
|
||||
chatBox.sendFormattedMessageToPlayer(
|
||||
safeParseTextComponent(config.warnToastConfig.msg, player),
|
||||
player,
|
||||
"AccessControl",
|
||||
"[]",
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
for (const targetPlayer of warnTargetPlayers) {
|
||||
if (!onlinePlayers.includes(targetPlayer)) continue;
|
||||
chatBox.sendFormattedMessageToPlayer(
|
||||
textutils.serialise({
|
||||
text: warnMsg,
|
||||
color: "red",
|
||||
} as MinecraftTextComponent),
|
||||
targetPlayer,
|
||||
"AccessControl",
|
||||
"[]",
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function sendCommandHelp(targetPlayer: string) {
|
||||
chatBox.sendMessageToPlayer(
|
||||
`
|
||||
Command Usage: @AC /<Command> [args]
|
||||
Command:
|
||||
- add <userGroup> <playerName>
|
||||
add player to group
|
||||
userGroup: ${groupNames.join(", ")}
|
||||
- del <userGroup> <playerName>
|
||||
delete player in the group, except Admin
|
||||
userGroup: ${groupNames.join(", ")}
|
||||
- list
|
||||
list all of the player with its group
|
||||
- set <options> [params]
|
||||
config access control settins
|
||||
options:
|
||||
- warnInterval <number>
|
||||
set the interval of warn, which is not allowed
|
||||
- detectInterval <number>
|
||||
set the interval of detecting players
|
||||
- detectRange <number>
|
||||
set the sphere range of detect
|
||||
`,
|
||||
targetPlayer,
|
||||
"AccessControl",
|
||||
"[]",
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
function warnLoop() {
|
||||
while (true) {
|
||||
for (const player of notAllowedPlayers) {
|
||||
if (inRangePlayers.includes(player)) {
|
||||
sendWarn(player);
|
||||
} else {
|
||||
notAllowedPlayers = notAllowedPlayers.filter(
|
||||
(value) => value != player,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
os.sleep(config.warnInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function mainLoop() {
|
||||
while (true) {
|
||||
const players = playerDetector.getPlayersInRange(config.detectRange);
|
||||
if (DEBUG) {
|
||||
const playersList = "[ " + players.join(",") + " ]";
|
||||
log.debug(`Detected ${players.length} players: ${playersList}`);
|
||||
}
|
||||
|
||||
for (const player of players) {
|
||||
if (inRangePlayers.includes(player)) continue;
|
||||
|
||||
if (config.adminGroupConfig.groupUsers.includes(player)) {
|
||||
log.info(`Admin ${player} enter`);
|
||||
sendToast(
|
||||
config.adminGroupConfig.toastConfig ?? config.defaultToastConfig,
|
||||
player,
|
||||
config.adminGroupConfig,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let inUserGroup = false;
|
||||
for (const userGroupConfig of config.usersGroups) {
|
||||
if (userGroupConfig.groupUsers == undefined) continue;
|
||||
if (!userGroupConfig.groupUsers.includes(player)) continue;
|
||||
|
||||
if (!userGroupConfig.isAllowed) {
|
||||
sendWarn(player);
|
||||
notAllowedPlayers.push(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
log.info(`${userGroupConfig.groupName} ${player} enter`);
|
||||
sendToast(
|
||||
userGroupConfig.toastConfig ?? config.defaultToastConfig,
|
||||
player,
|
||||
userGroupConfig,
|
||||
);
|
||||
|
||||
inUserGroup = true;
|
||||
}
|
||||
if (inUserGroup) continue;
|
||||
|
||||
sendWarn(player);
|
||||
notAllowedPlayers.push(player);
|
||||
}
|
||||
|
||||
inRangePlayers = players;
|
||||
os.sleep(config.detectInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function configLoop() {
|
||||
while (true) {
|
||||
const ev = pullEventAs(ChatBoxEvent, "chat");
|
||||
|
||||
if (ev == undefined) continue;
|
||||
if (!config.adminGroupConfig.groupUsers.includes(ev.username)) continue;
|
||||
if (!ev.message.startsWith("@AC")) continue;
|
||||
// log.info(`Received "${ev.message}" from admin ${ev.username}`);
|
||||
|
||||
const params = ev.message.split(" ");
|
||||
if (params.length < 2) {
|
||||
sendCommandHelp(ev.username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (params[1] == "/add" && params.length == 4) {
|
||||
if (params[2] == "admin") {
|
||||
config.adminGroupConfig.groupUsers.push(params[3]);
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Add player ${params[3]} to admin`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else if (groupNames.includes(params[2])) {
|
||||
const groupConfig = config.usersGroups.find(
|
||||
(value) => value.groupName == params[2],
|
||||
)!;
|
||||
|
||||
if (groupConfig.groupUsers == undefined)
|
||||
groupConfig.groupUsers = [params[3]];
|
||||
else groupConfig.groupUsers.push(params[3]);
|
||||
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Add player ${params[3]} to ${groupConfig.groupName}`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else {
|
||||
sendCommandHelp(ev.username);
|
||||
continue;
|
||||
}
|
||||
} else if (params[1] == "/del" && params.length == 4) {
|
||||
if (params[2] == "admin") {
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Could't delete admin, please edit config`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else if (groupNames.includes(params[2])) {
|
||||
const groupConfig = config.usersGroups.find(
|
||||
(value) => value.groupName == params[2],
|
||||
)!;
|
||||
|
||||
if (groupConfig.groupUsers == undefined) groupConfig.groupUsers = [];
|
||||
else
|
||||
groupConfig.groupUsers = groupConfig.groupUsers.filter(
|
||||
(user) => user != params[3],
|
||||
);
|
||||
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Delete ${groupConfig.groupName} ${params[3]}`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else {
|
||||
sendCommandHelp(ev.username);
|
||||
continue;
|
||||
}
|
||||
} else if (params[1] == "/list") {
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Admins : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
for (const groupConfig of config.usersGroups) {
|
||||
chatBox.sendMessageToPlayer(
|
||||
`${groupConfig.groupName} : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
}
|
||||
} else if (params[1] == "/set" && params.length == 4) {
|
||||
if (params[2] == "warnInterval") {
|
||||
config.warnInterval = parseInt(params[3]);
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Set warn interval to ${config.warnInterval}`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else if (params[2] == "detectInterval") {
|
||||
config.detectInterval = parseInt(params[3]);
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Set detect interval to ${config.detectInterval}`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else if (params[2] == "detectRange") {
|
||||
config.detectRange = parseInt(params[3]);
|
||||
chatBox.sendMessageToPlayer(
|
||||
`Set detect range to ${config.detectRange}`,
|
||||
ev.username,
|
||||
"AccessControl",
|
||||
);
|
||||
} else {
|
||||
sendCommandHelp(ev.username);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
sendCommandHelp(ev.username);
|
||||
continue;
|
||||
}
|
||||
|
||||
saveConfig(config, configFilepath);
|
||||
}
|
||||
}
|
||||
|
||||
function main(args: string[]) {
|
||||
log.debug("Starting access control system, get args: " + args.join(", "));
|
||||
if (args.length == 1) {
|
||||
if (args[0] == "start") {
|
||||
parallel.waitForAll(
|
||||
() => {
|
||||
mainLoop();
|
||||
},
|
||||
() => {
|
||||
configLoop();
|
||||
},
|
||||
() => {
|
||||
warnLoop();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
print(`Usage: accesscontrol start`);
|
||||
}
|
||||
|
||||
try {
|
||||
main(args);
|
||||
} catch (error: unknown) {
|
||||
log.error(textutils.serialise(error as object));
|
||||
} finally {
|
||||
log.close();
|
||||
}
|
||||
@@ -58,7 +58,7 @@ function main() {
|
||||
|
||||
// Get package NBT
|
||||
packagesContainer.pushItems(turtleLocalName, slot);
|
||||
const packageInfo = blockReader.getBlockData().Items[1];
|
||||
const packageInfo = blockReader.getBlockData()!.Items[1];
|
||||
// log.info(textutils.serialise(packageInfo));
|
||||
|
||||
// Get recipe
|
||||
@@ -96,7 +96,7 @@ function main() {
|
||||
restCraftCnt -= craftCnt;
|
||||
|
||||
// Get output item
|
||||
craftOutputItem ??= blockReader.getBlockData().Items[1];
|
||||
craftOutputItem ??= blockReader.getBlockData()!.Items[1];
|
||||
} while (restCraftCnt > 0);
|
||||
|
||||
// Finally output
|
||||
|
||||
@@ -1,43 +1,62 @@
|
||||
type PeripheralType = "inventory" | "modem" | "wiredModem" | "blockReader";
|
||||
type PeripheralType =
|
||||
| "inventory"
|
||||
| "modem"
|
||||
| "wiredModem"
|
||||
| "blockReader"
|
||||
| "chatBox"
|
||||
| "playerDetector";
|
||||
type BlockSide = "top" | "bottom" | "left" | "right" | "front" | "back";
|
||||
|
||||
// Declare the function signature for findBySide
|
||||
function findByName(
|
||||
devType: "inventory",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): InventoryPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: "modem",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): ModemPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: "wiredModem",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): WiredModemPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: "blockReader",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): BlockReaderPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: "chatBox",
|
||||
devName?: string,
|
||||
): ChatBoxPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: "playerDetector",
|
||||
devName?: string,
|
||||
): PlayerDetectorPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: PeripheralType,
|
||||
side: BlockSide,
|
||||
): IPeripheral | undefined;
|
||||
function findByName(
|
||||
devType: PeripheralType,
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): IPeripheral | undefined;
|
||||
|
||||
// Implement the function signature for findBySide
|
||||
// Implement the function signature for findByName
|
||||
function findByName(
|
||||
devType: PeripheralType,
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): IPeripheral | undefined {
|
||||
const dev = peripheral.find(
|
||||
devType == "wiredModem" ? "modem" : devType,
|
||||
(name: string, _) => {
|
||||
return name == devName;
|
||||
},
|
||||
)[0];
|
||||
let dev;
|
||||
if (devName == undefined) {
|
||||
dev = peripheral.find(devType)[0];
|
||||
} else {
|
||||
dev = peripheral.find(
|
||||
devType == "wiredModem" ? "modem" : devType,
|
||||
(name: string, _) => {
|
||||
return name == devName;
|
||||
},
|
||||
)[0];
|
||||
}
|
||||
|
||||
// Seperate Modem and wiredModem
|
||||
if (
|
||||
@@ -54,41 +73,54 @@ function findByName(
|
||||
)
|
||||
return undefined;
|
||||
|
||||
if (dev != undefined && peripheral.getType(dev) != devType) return undefined;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
// Declare the function signature for findBySideRequired
|
||||
function findByNameRequired(
|
||||
devType: "inventory",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): InventoryPeripheral;
|
||||
function findByNameRequired(devType: "modem", devName: string): ModemPeripheral;
|
||||
function findByNameRequired(
|
||||
devType: "modem",
|
||||
devName?: string,
|
||||
): ModemPeripheral;
|
||||
function findByNameRequired(
|
||||
devType: "wiredModem",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): WiredModemPeripheral;
|
||||
function findByNameRequired(
|
||||
devType: "blockReader",
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): BlockReaderPeripheral;
|
||||
function findByNameRequired(
|
||||
devType: "chatBox",
|
||||
devName?: string,
|
||||
): ChatBoxPeripheral;
|
||||
function findByNameRequired(
|
||||
devType: "playerDetector",
|
||||
devName?: string,
|
||||
): PlayerDetectorPeripheral;
|
||||
function findByNameRequired<T extends IPeripheral>(
|
||||
devType: PeripheralType,
|
||||
side: BlockSide,
|
||||
): T;
|
||||
function findByNameRequired<T extends IPeripheral>(
|
||||
devType: PeripheralType,
|
||||
devName: string,
|
||||
devName?: string,
|
||||
): T;
|
||||
|
||||
// Implement the function signature for findBySideRequired
|
||||
function findByNameRequired<T extends IPeripheral>(
|
||||
devType: PeripheralType,
|
||||
side: string,
|
||||
devName?: string,
|
||||
): T {
|
||||
const dev = findByName(devType, side);
|
||||
if (!dev) {
|
||||
const dev = findByName(devType, devName);
|
||||
if (dev == undefined) {
|
||||
throw new Error(
|
||||
`Required peripheral of type '${devType}' not found on side '${side}'`,
|
||||
`Required peripheral of type '${devType}' not found with name '${devName}'`,
|
||||
);
|
||||
}
|
||||
return dev as T;
|
||||
|
||||
112
src/lib/ccLog.ts
112
src/lib/ccLog.ts
@@ -1,16 +1,35 @@
|
||||
enum LogLevel {
|
||||
Info = 0,
|
||||
Warn = 1,
|
||||
Error = 2,
|
||||
Debug = 0,
|
||||
Info = 1,
|
||||
Warn = 2,
|
||||
Error = 3,
|
||||
}
|
||||
|
||||
// Define time interval constants in seconds
|
||||
export const SECOND = 1;
|
||||
export const MINUTE = 60 * SECOND;
|
||||
export const HOUR = 60 * MINUTE;
|
||||
export const DAY = 24 * HOUR;
|
||||
export const WEEK = 7 * DAY;
|
||||
|
||||
export class CCLog {
|
||||
private fp: LuaFile | undefined;
|
||||
constructor(filename?: string) {
|
||||
private filename?: string;
|
||||
private interval: number;
|
||||
private startTime: number;
|
||||
private currentTimePeriod: string;
|
||||
|
||||
constructor(filename?: string, interval: number = DAY) {
|
||||
term.clear();
|
||||
term.setCursorPos(1, 1);
|
||||
|
||||
this.interval = interval;
|
||||
this.startTime = os.time(os.date("*t"));
|
||||
this.currentTimePeriod = this.getTimePeriodString(this.startTime);
|
||||
|
||||
if (filename != undefined && filename.length != 0) {
|
||||
const filepath = shell.dir() + "/" + filename;
|
||||
this.filename = filename;
|
||||
const filepath = this.generateFilePath(filename, this.currentTimePeriod);
|
||||
const [file, error] = io.open(filepath, fs.exists(filepath) ? "a" : "w+");
|
||||
if (file != undefined) {
|
||||
this.fp = file;
|
||||
@@ -20,12 +39,84 @@ export class CCLog {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a time period string based on the interval
|
||||
* For DAY interval: YYYY-MM-DD
|
||||
* For HOUR interval: YYYY-MM-DD-HH
|
||||
* For MINUTE interval: YYYY-MM-DD-HH-MM
|
||||
* For SECOND interval: YYYY-MM-DD-HH-MM-SS
|
||||
*/
|
||||
private getTimePeriodString(time: number): string {
|
||||
// Calculate which time period this timestamp falls into
|
||||
const periodStart = Math.floor(time / this.interval) * this.interval;
|
||||
const periodDate = os.date("*t", periodStart);
|
||||
|
||||
if (this.interval >= DAY) {
|
||||
return `${periodDate.year}-${String(periodDate.month).padStart(2, "0")}-${String(periodDate.day).padStart(2, "0")}`;
|
||||
} else {
|
||||
return `[${periodDate.year}-${String(periodDate.month).padStart(2, "0")}-${String(periodDate.day).padStart(2, "0")}] - [${String(periodDate.hour).padStart(2, "0")}-${String(periodDate.min).padStart(2, "0")}-${String(periodDate.sec).padStart(2, "0")}]`;
|
||||
}
|
||||
}
|
||||
|
||||
private generateFilePath(baseFilename: string, timePeriod: string): string {
|
||||
// Extract file extension if present
|
||||
const fileNameSubStrings = baseFilename.split(".");
|
||||
let filenameWithoutExt: string;
|
||||
let extension = "";
|
||||
|
||||
if (fileNameSubStrings.length > 1) {
|
||||
filenameWithoutExt = fileNameSubStrings[0];
|
||||
extension = fileNameSubStrings[1];
|
||||
} else {
|
||||
filenameWithoutExt = baseFilename;
|
||||
}
|
||||
|
||||
return `${shell.dir()}/${filenameWithoutExt}[${timePeriod}].${extension}`;
|
||||
}
|
||||
|
||||
private checkAndRotateLogFile() {
|
||||
if (this.filename != undefined && this.filename.length != 0) {
|
||||
const currentTime = os.time(os.date("*t"));
|
||||
const currentTimePeriod = this.getTimePeriodString(currentTime);
|
||||
|
||||
// If we're in a new time period, rotate the log file
|
||||
if (currentTimePeriod !== this.currentTimePeriod) {
|
||||
// Close current file if open
|
||||
if (this.fp) {
|
||||
this.fp.close();
|
||||
this.fp = undefined;
|
||||
}
|
||||
|
||||
// Update the current time period
|
||||
this.currentTimePeriod = currentTimePeriod;
|
||||
|
||||
// Open new log file for the new time period
|
||||
const filepath = this.generateFilePath(
|
||||
this.filename,
|
||||
this.currentTimePeriod,
|
||||
);
|
||||
const [file, error] = io.open(
|
||||
filepath,
|
||||
fs.exists(filepath) ? "a" : "w+",
|
||||
);
|
||||
if (file != undefined) {
|
||||
this.fp = file;
|
||||
} else {
|
||||
throw Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getFormatMsg(msg: string, level: LogLevel): string {
|
||||
const date = os.date("*t");
|
||||
return `[ ${date.year}/${date.month}/${date.day} -- ${date.hour}:${date.min}:${date.sec} ${LogLevel[level]} ] : ${msg}\r\n`;
|
||||
return `[ ${date.year}/${String(date.month).padStart(2, "0")}/${String(date.day).padStart(2, "0")} ${String(date.hour).padStart(2, "0")}:${String(date.min).padStart(2, "0")}:${String(date.sec).padStart(2, "0")} ${LogLevel[level]} ] : ${msg}`;
|
||||
}
|
||||
|
||||
public writeLine(msg: string, color?: Color) {
|
||||
// Check if we need to rotate the log file
|
||||
this.checkAndRotateLogFile();
|
||||
|
||||
let originalColor: Color = 0;
|
||||
if (color != undefined) {
|
||||
originalColor = term.getTextColor();
|
||||
@@ -33,17 +124,18 @@ export class CCLog {
|
||||
}
|
||||
|
||||
// Log
|
||||
term.write(msg);
|
||||
print(msg);
|
||||
if (this.fp != undefined) {
|
||||
this.fp.write(msg);
|
||||
this.fp.write(msg + "\r\n");
|
||||
}
|
||||
|
||||
if (color != undefined) {
|
||||
term.setTextColor(originalColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Next line
|
||||
term.setCursorPos(1, term.getCursorPos()[1] + 1);
|
||||
public debug(msg: string) {
|
||||
this.writeLine(this.getFormatMsg(msg, LogLevel.Debug), colors.gray);
|
||||
}
|
||||
|
||||
public info(msg: string) {
|
||||
|
||||
25
src/lib/ccTime.ts
Normal file
25
src/lib/ccTime.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
class ccDate {
|
||||
private _timestamp: number;
|
||||
|
||||
constructor() {
|
||||
this._timestamp = os.time(os.date("*t"));
|
||||
}
|
||||
|
||||
public static toDateTable(timestamp: number): LuaDate {
|
||||
return os.date("*t", timestamp) as LuaDate;
|
||||
}
|
||||
|
||||
public toDateTable(): LuaDate {
|
||||
return os.date("*t", this._timestamp) as LuaDate;
|
||||
}
|
||||
|
||||
public static toTimestamp(date: LuaDate): number {
|
||||
return os.time(date);
|
||||
}
|
||||
|
||||
public toTimestamp(): number {
|
||||
return this._timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
export { ccDate };
|
||||
772
src/lib/event.ts
772
src/lib/event.ts
@@ -2,312 +2,461 @@
|
||||
// delete them from eventInitializers as well.
|
||||
|
||||
export interface IEvent {
|
||||
get_name(): string;
|
||||
get_args(): any[];
|
||||
get_name(): string;
|
||||
get_args(): unknown[];
|
||||
}
|
||||
|
||||
export class CharEvent implements IEvent {
|
||||
public character: string = "";
|
||||
public get_name() {return "char";}
|
||||
public get_args() {return [this.character];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "char") return null;
|
||||
let ev = new CharEvent();
|
||||
ev.character = (args[1] as string);
|
||||
return ev;
|
||||
}
|
||||
public character = "";
|
||||
public get_name() {
|
||||
return "char";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.character];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || args[0] != "char") return undefined;
|
||||
const ev = new CharEvent();
|
||||
ev.character = args[1] as string;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class KeyEvent implements IEvent {
|
||||
public key: Key = 0;
|
||||
public isHeld: boolean = false;
|
||||
public isUp: boolean = false;
|
||||
public get_name() {return this.isUp ? "key_up" : "key";}
|
||||
public get_args() {return [this.key, (this.isUp ? null : this.isHeld)];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "key" && (args[0] as string) != "key_up")) return null;
|
||||
let ev = new KeyEvent();
|
||||
ev.key = (args[1] as number);
|
||||
ev.isUp = (args[0] as string) == "key_up";
|
||||
ev.isHeld = ev.isUp ? false : (args[2] as boolean);
|
||||
return ev;
|
||||
}
|
||||
public key: Key = 0;
|
||||
public isHeld = false;
|
||||
public isUp = false;
|
||||
public get_name() {
|
||||
return this.isUp ? "key_up" : "key";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.key, this.isUp ? undefined : this.isHeld];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
(args[0] != "key" && args[0] != "key_up")
|
||||
)
|
||||
return undefined;
|
||||
const ev = new KeyEvent();
|
||||
ev.key = args[1] as number;
|
||||
ev.isUp = (args[0] as string) == "key_up";
|
||||
ev.isHeld = ev.isUp ? false : (args[2] as boolean);
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class PasteEvent implements IEvent {
|
||||
public text: string = "";
|
||||
public get_name() {return "paste";}
|
||||
public get_args() {return [(this.text as any)];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "paste") return null;
|
||||
let ev = new PasteEvent();
|
||||
ev.text = (args[1] as string);
|
||||
return ev;
|
||||
}
|
||||
public text = "";
|
||||
public get_name() {
|
||||
return "paste";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.text];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || args[0] != "paste") return undefined;
|
||||
const ev = new PasteEvent();
|
||||
ev.text = args[1] as string;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class TimerEvent implements IEvent {
|
||||
public id: number = 0;
|
||||
public isAlarm: boolean = false;
|
||||
public get_name() {return this.isAlarm ? "alarm" : "timer";}
|
||||
public get_args() {return [this.id];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "timer" && (args[0] as string) != "alarm")) return null;
|
||||
let ev = new TimerEvent();
|
||||
ev.id = (args[1] as number);
|
||||
ev.isAlarm = (args[0] as string) == "alarm";
|
||||
return ev;
|
||||
}
|
||||
public id = 0;
|
||||
public isAlarm = false;
|
||||
public get_name() {
|
||||
return this.isAlarm ? "alarm" : "timer";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.id];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
(args[0] != "timer" && args[0] != "alarm")
|
||||
)
|
||||
return undefined;
|
||||
const ev = new TimerEvent();
|
||||
ev.id = args[1] as number;
|
||||
ev.isAlarm = args[0] == "alarm";
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class TaskCompleteEvent implements IEvent {
|
||||
public id: number = 0;
|
||||
public success: boolean = false;
|
||||
public error: string | null = null;
|
||||
public params: any[] = [];
|
||||
public get_name() {return "task_complete";}
|
||||
public get_args() {
|
||||
if (this.success) return [this.id, this.success].concat(this.params);
|
||||
else return [this.id, this.success, this.error];
|
||||
}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "task_complete") return null;
|
||||
let ev = new TaskCompleteEvent();
|
||||
ev.id = (args[1] as number);
|
||||
ev.success = (args[2] as boolean);
|
||||
if (ev.success) {
|
||||
ev.error = null;
|
||||
ev.params = args.slice(3);
|
||||
} else {
|
||||
ev.error = (args[3] as string);
|
||||
ev.params = [];
|
||||
}
|
||||
return ev;
|
||||
public id = 0;
|
||||
public success = false;
|
||||
public error: string | undefined = undefined;
|
||||
public params: any[] = [];
|
||||
public get_name() {
|
||||
return "task_complete";
|
||||
}
|
||||
public get_args() {
|
||||
if (this.success) return [this.id, this.success].concat(this.params);
|
||||
else return [this.id, this.success, this.error];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || args[0] != "task_complete")
|
||||
return undefined;
|
||||
const ev = new TaskCompleteEvent();
|
||||
ev.id = args[1] as number;
|
||||
ev.success = args[2] as boolean;
|
||||
if (ev.success) {
|
||||
ev.error = undefined;
|
||||
ev.params = args.slice(3);
|
||||
} else {
|
||||
ev.error = args[3] as string;
|
||||
ev.params = [];
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class RedstoneEvent implements IEvent {
|
||||
public get_name() {return "redstone";}
|
||||
public get_args() {return [];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "redstone") return null;
|
||||
let ev = new RedstoneEvent();
|
||||
return ev;
|
||||
}
|
||||
public get_name() {
|
||||
return "redstone";
|
||||
}
|
||||
public get_args() {
|
||||
return [];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "redstone")
|
||||
return undefined;
|
||||
let ev = new RedstoneEvent();
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class TerminateEvent implements IEvent {
|
||||
public get_name() {return "terminate";}
|
||||
public get_args() {return [];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "terminate") return null;
|
||||
let ev = new TerminateEvent();
|
||||
return ev;
|
||||
}
|
||||
public get_name() {
|
||||
return "terminate";
|
||||
}
|
||||
public get_args() {
|
||||
return [];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "terminate")
|
||||
return undefined;
|
||||
let ev = new TerminateEvent();
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class DiskEvent implements IEvent {
|
||||
public side: string = "";
|
||||
public eject: boolean = false;
|
||||
public get_name() {return this.eject ? "disk_eject" : "disk";}
|
||||
public get_args() {return [this.side];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "disk" && (args[0] as string) != "disk_eject")) return null;
|
||||
let ev = new DiskEvent();
|
||||
ev.side = (args[1] as string);
|
||||
ev.eject = (args[0] as string) == "disk_eject";
|
||||
return ev;
|
||||
}
|
||||
public side: string = "";
|
||||
public eject: boolean = false;
|
||||
public get_name() {
|
||||
return this.eject ? "disk_eject" : "disk";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.side];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
((args[0] as string) != "disk" && (args[0] as string) != "disk_eject")
|
||||
)
|
||||
return undefined;
|
||||
let ev = new DiskEvent();
|
||||
ev.side = args[1] as string;
|
||||
ev.eject = (args[0] as string) == "disk_eject";
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class PeripheralEvent implements IEvent {
|
||||
public side: string = "";
|
||||
public detach: boolean = false;
|
||||
public get_name() {return this.detach ? "peripheral_detach" : "peripheral";}
|
||||
public get_args() {return [this.side];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "peripheral" && (args[0] as string) != "peripheral_detach")) return null;
|
||||
let ev = new PeripheralEvent();
|
||||
ev.side = (args[1] as string);
|
||||
ev.detach = (args[0] as string) == "peripheral_detach";
|
||||
return ev;
|
||||
}
|
||||
public side: string = "";
|
||||
public detach: boolean = false;
|
||||
public get_name() {
|
||||
return this.detach ? "peripheral_detach" : "peripheral";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.side];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
((args[0] as string) != "peripheral" &&
|
||||
(args[0] as string) != "peripheral_detach")
|
||||
)
|
||||
return undefined;
|
||||
let ev = new PeripheralEvent();
|
||||
ev.side = args[1] as string;
|
||||
ev.detach = (args[0] as string) == "peripheral_detach";
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class RednetMessageEvent implements IEvent {
|
||||
public sender: number = 0;
|
||||
public message: any;
|
||||
public protocol: string | null = null;
|
||||
public get_name() {return "rednet_message";}
|
||||
public get_args() {return [this.sender, this.message, this.protocol];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "rednet_message") return null;
|
||||
let ev = new RednetMessageEvent();
|
||||
ev.sender = (args[1] as number);
|
||||
ev.message = args[2];
|
||||
ev.protocol = (args[3] as string);
|
||||
return ev;
|
||||
}
|
||||
public sender: number = 0;
|
||||
public message: any;
|
||||
public protocol: string | undefined = undefined;
|
||||
public get_name() {
|
||||
return "rednet_message";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.sender, this.message, this.protocol];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
(args[0] as string) != "rednet_message"
|
||||
)
|
||||
return undefined;
|
||||
let ev = new RednetMessageEvent();
|
||||
ev.sender = args[1] as number;
|
||||
ev.message = args[2];
|
||||
ev.protocol = args[3] as string;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class ModemMessageEvent implements IEvent {
|
||||
public side: string = "";
|
||||
public channel: number = 0;
|
||||
public replyChannel: number = 0;
|
||||
public message: any;
|
||||
public distance: number = 0;
|
||||
public get_name() {return "modem_message";}
|
||||
public get_args() {return [this.side, this.channel, this.replyChannel, this.message, this.distance];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "modem_message") return null;
|
||||
let ev = new ModemMessageEvent();
|
||||
ev.side = (args[1] as string);
|
||||
ev.channel = (args[2] as number);
|
||||
ev.replyChannel = (args[3] as number);
|
||||
ev.message = args[4];
|
||||
ev.distance = (args[5] as number);
|
||||
return ev;
|
||||
}
|
||||
public side: string = "";
|
||||
public channel: number = 0;
|
||||
public replyChannel: number = 0;
|
||||
public message: any;
|
||||
public distance: number = 0;
|
||||
public get_name() {
|
||||
return "modem_message";
|
||||
}
|
||||
public get_args() {
|
||||
return [
|
||||
this.side,
|
||||
this.channel,
|
||||
this.replyChannel,
|
||||
this.message,
|
||||
this.distance,
|
||||
];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
(args[0] as string) != "modem_message"
|
||||
)
|
||||
return undefined;
|
||||
let ev = new ModemMessageEvent();
|
||||
ev.side = args[1] as string;
|
||||
ev.channel = args[2] as number;
|
||||
ev.replyChannel = args[3] as number;
|
||||
ev.message = args[4];
|
||||
ev.distance = args[5] as number;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class HTTPEvent implements IEvent {
|
||||
public url: string = "";
|
||||
public handle: HTTPResponse | null = null;
|
||||
public error: string | null = null;
|
||||
public get_name() {return this.error == null ? "http_success" : "http_failure";}
|
||||
public get_args() {return [this.url, (this.error == null ? this.handle : this.error), (this.error != null ? this.handle : null)];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "http_success" && (args[0] as string) != "http_failure")) return null;
|
||||
let ev = new HTTPEvent();
|
||||
ev.url = (args[1] as string);
|
||||
if ((args[0] as string) == "http_success") {
|
||||
ev.error = null;
|
||||
ev.handle = (args[2] as HTTPResponse);
|
||||
} else {
|
||||
ev.error = (args[2] as string);
|
||||
if (ev.error == null) ev.error = "";
|
||||
ev.handle = (args[3] as HTTPResponse);
|
||||
}
|
||||
return ev;
|
||||
public url: string = "";
|
||||
public handle: HTTPResponse | undefined = undefined;
|
||||
public error: string | undefined = undefined;
|
||||
public get_name() {
|
||||
return this.error == undefined ? "http_success" : "http_failure";
|
||||
}
|
||||
public get_args() {
|
||||
return [
|
||||
this.url,
|
||||
this.error == undefined ? this.handle : this.error,
|
||||
this.error != undefined ? this.handle : undefined,
|
||||
];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
((args[0] as string) != "http_success" &&
|
||||
(args[0] as string) != "http_failure")
|
||||
)
|
||||
return undefined;
|
||||
let ev = new HTTPEvent();
|
||||
ev.url = args[1] as string;
|
||||
if ((args[0] as string) == "http_success") {
|
||||
ev.error = undefined;
|
||||
ev.handle = args[2] as HTTPResponse;
|
||||
} else {
|
||||
ev.error = args[2] as string;
|
||||
if (ev.error == undefined) ev.error = "";
|
||||
ev.handle = args[3] as HTTPResponse;
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebSocketEvent implements IEvent {
|
||||
public handle: WebSocket | null = null;
|
||||
public error: string | null = null;
|
||||
public get_name() {return this.error == null ? "websocket_success" : "websocket_failure";}
|
||||
public get_args() {return [this.handle == null ? this.error : this.handle];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "websocket_success" && (args[0] as string) != "websocket_failure")) return null;
|
||||
let ev = new WebSocketEvent();
|
||||
if ((args[0] as string) == "websocket_success") {
|
||||
ev.handle = (args[1] as WebSocket);
|
||||
ev.error = null;
|
||||
} else {
|
||||
ev.error = (args[1] as string);
|
||||
ev.handle = null;
|
||||
}
|
||||
return ev;
|
||||
public handle: WebSocket | undefined = undefined;
|
||||
public error: string | undefined = undefined;
|
||||
public get_name() {
|
||||
return this.error == undefined ? "websocket_success" : "websocket_failure";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.handle == undefined ? this.error : this.handle];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
((args[0] as string) != "websocket_success" &&
|
||||
(args[0] as string) != "websocket_failure")
|
||||
)
|
||||
return undefined;
|
||||
let ev = new WebSocketEvent();
|
||||
if ((args[0] as string) == "websocket_success") {
|
||||
ev.handle = args[1] as WebSocket;
|
||||
ev.error = undefined;
|
||||
} else {
|
||||
ev.error = args[1] as string;
|
||||
ev.handle = undefined;
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export enum MouseEventType {
|
||||
Click,
|
||||
Up,
|
||||
Scroll,
|
||||
Drag,
|
||||
Touch,
|
||||
Move,
|
||||
Click,
|
||||
Up,
|
||||
Scroll,
|
||||
Drag,
|
||||
Touch,
|
||||
Move,
|
||||
}
|
||||
|
||||
export class MouseEvent implements IEvent {
|
||||
public button: number = 0;
|
||||
public x: number = 0;
|
||||
public y: number = 0;
|
||||
public side: string | null = null;
|
||||
public type: MouseEventType = MouseEventType.Click;
|
||||
public get_name() {
|
||||
return {
|
||||
[MouseEventType.Click]: "mouse_click",
|
||||
[MouseEventType.Up]: "mouse_up",
|
||||
[MouseEventType.Scroll]: "mouse_scroll",
|
||||
[MouseEventType.Drag]: "mouse_drag",
|
||||
[MouseEventType.Touch]: "monitor_touch",
|
||||
[MouseEventType.Move]: "mouse_move"
|
||||
}[this.type];
|
||||
}
|
||||
public get_args() {return [(this.type == MouseEventType.Touch ? this.side : this.button), this.x, this.y];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string")) return null;
|
||||
let ev = new MouseEvent();
|
||||
const type = args[0] as string;
|
||||
if (type == "mouse_click") {ev.type = MouseEventType.Click; ev.button = (args[1] as number); ev.side = null;}
|
||||
else if (type == "mouse_up") {ev.type = MouseEventType.Up; ev.button = (args[1] as number); ev.side = null;}
|
||||
else if (type == "mouse_scroll") {ev.type = MouseEventType.Scroll; ev.button = (args[1] as number); ev.side = null;}
|
||||
else if (type == "mouse_drag") {ev.type = MouseEventType.Drag; ev.button = (args[1] as number); ev.side = null;}
|
||||
else if (type == "monitor_touch") {ev.type = MouseEventType.Touch; ev.button = 0; ev.side = (args[1] as string);}
|
||||
else if (type == "mouse_move") {ev.type = MouseEventType.Move; ev.button = (args[1] as number); ev.side = null;}
|
||||
else return null;
|
||||
ev.x = (args[2] as number);
|
||||
ev.y = (args[3] as number);
|
||||
return ev;
|
||||
}
|
||||
public button: number = 0;
|
||||
public x: number = 0;
|
||||
public y: number = 0;
|
||||
public side: string | undefined = undefined;
|
||||
public type: MouseEventType = MouseEventType.Click;
|
||||
public get_name() {
|
||||
return {
|
||||
[MouseEventType.Click]: "mouse_click",
|
||||
[MouseEventType.Up]: "mouse_up",
|
||||
[MouseEventType.Scroll]: "mouse_scroll",
|
||||
[MouseEventType.Drag]: "mouse_drag",
|
||||
[MouseEventType.Touch]: "monitor_touch",
|
||||
[MouseEventType.Move]: "mouse_move",
|
||||
}[this.type];
|
||||
}
|
||||
public get_args() {
|
||||
return [
|
||||
this.type == MouseEventType.Touch ? this.side : this.button,
|
||||
this.x,
|
||||
this.y,
|
||||
];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string")) return undefined;
|
||||
let ev = new MouseEvent();
|
||||
const type = args[0] as string;
|
||||
if (type == "mouse_click") {
|
||||
ev.type = MouseEventType.Click;
|
||||
ev.button = args[1] as number;
|
||||
ev.side = undefined;
|
||||
} else if (type == "mouse_up") {
|
||||
ev.type = MouseEventType.Up;
|
||||
ev.button = args[1] as number;
|
||||
ev.side = undefined;
|
||||
} else if (type == "mouse_scroll") {
|
||||
ev.type = MouseEventType.Scroll;
|
||||
ev.button = args[1] as number;
|
||||
ev.side = undefined;
|
||||
} else if (type == "mouse_drag") {
|
||||
ev.type = MouseEventType.Drag;
|
||||
ev.button = args[1] as number;
|
||||
ev.side = undefined;
|
||||
} else if (type == "monitor_touch") {
|
||||
ev.type = MouseEventType.Touch;
|
||||
ev.button = 0;
|
||||
ev.side = args[1] as string;
|
||||
} else if (type == "mouse_move") {
|
||||
ev.type = MouseEventType.Move;
|
||||
ev.button = args[1] as number;
|
||||
ev.side = undefined;
|
||||
} else return undefined;
|
||||
ev.x = args[2] as number;
|
||||
ev.y = args[3] as number;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class ResizeEvent implements IEvent {
|
||||
public side: string | null = null;
|
||||
public get_name() {return this.side == null ? "term_resize" : "monitor_resize";}
|
||||
public get_args() {return [this.side];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || ((args[0] as string) != "term_resize" && (args[0] as string) != "monitor_resize")) return null;
|
||||
let ev = new ResizeEvent();
|
||||
if ((args[0] as string) == "monitor_resize") ev.side = (args[1] as string);
|
||||
else ev.side = null;
|
||||
return ev;
|
||||
}
|
||||
public side: string | undefined = undefined;
|
||||
public get_name() {
|
||||
return this.side == undefined ? "term_resize" : "monitor_resize";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.side];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
((args[0] as string) != "term_resize" &&
|
||||
(args[0] as string) != "monitor_resize")
|
||||
)
|
||||
return undefined;
|
||||
let ev = new ResizeEvent();
|
||||
if ((args[0] as string) == "monitor_resize") ev.side = args[1] as string;
|
||||
else ev.side = undefined;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
export class TurtleInventoryEvent implements IEvent {
|
||||
public get_name() {return "turtle_inventory";}
|
||||
public get_args() {return [];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "turtle_inventory") return null;
|
||||
let ev = new TurtleInventoryEvent();
|
||||
return ev;
|
||||
}
|
||||
public get_name() {
|
||||
return "turtle_inventory";
|
||||
}
|
||||
public get_args() {
|
||||
return [];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (
|
||||
!(typeof args[0] === "string") ||
|
||||
(args[0] as string) != "turtle_inventory"
|
||||
)
|
||||
return undefined;
|
||||
let ev = new TurtleInventoryEvent();
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
class SpeakerAudioEmptyEvent implements IEvent {
|
||||
public side: string = "";
|
||||
public get_name() {return "speaker_audio_empty";}
|
||||
public get_args() {return [this.side];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "speaker_audio_empty") return null;
|
||||
let ev: SpeakerAudioEmptyEvent;
|
||||
ev.side = args[1] as string;
|
||||
return ev;
|
||||
}
|
||||
public side = "";
|
||||
public get_name() {
|
||||
return "speaker_audio_empty";
|
||||
}
|
||||
public get_args() {
|
||||
return [this.side];
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || args[0] != "speaker_audio_empty")
|
||||
return undefined;
|
||||
const ev = new SpeakerAudioEmptyEvent();
|
||||
ev.side = args[1] as string;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
class ComputerCommandEvent implements IEvent {
|
||||
public args: string[] = [];
|
||||
public get_name() {return "computer_command";}
|
||||
public get_args() {return this.args;}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "computer_command") return null;
|
||||
let ev: ComputerCommandEvent;
|
||||
ev.args = args.slice(1);
|
||||
return ev;
|
||||
}
|
||||
public args: string[] = [];
|
||||
public get_name() {
|
||||
return "computer_command";
|
||||
}
|
||||
public get_args() {
|
||||
return this.args;
|
||||
}
|
||||
public static init(args: unknown[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || args[0] != "computer_command")
|
||||
return undefined;
|
||||
const ev = new ComputerCommandEvent();
|
||||
ev.args = args.slice(1) as string[];
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class Event implements IEvent {
|
||||
|
||||
|
||||
public get_name() {return "";}
|
||||
public get_args() {return [(: any)];}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "") return null;
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "") return undefined;
|
||||
let ev: Event;
|
||||
|
||||
return ev;
|
||||
@@ -315,60 +464,107 @@ class Event implements IEvent {
|
||||
}
|
||||
*/
|
||||
|
||||
export class GenericEvent implements IEvent {
|
||||
public args: any[] = [];
|
||||
public get_name() {return (this.args[0] as string);}
|
||||
public get_args() {return this.args.slice(1);}
|
||||
public static init(args: any[]): IEvent | null {
|
||||
let ev = new GenericEvent();
|
||||
ev.args = args;
|
||||
return ev;
|
||||
}
|
||||
export class ChatBoxEvent implements IEvent {
|
||||
public username: string = "";
|
||||
public message: string = "";
|
||||
public uuid: string = "";
|
||||
public isHidden: boolean = false;
|
||||
public messageUtf8: string = "";
|
||||
|
||||
public get_name() {
|
||||
return "chat";
|
||||
}
|
||||
public get_args() {
|
||||
return [
|
||||
this.username,
|
||||
this.message,
|
||||
this.uuid,
|
||||
this.isHidden,
|
||||
this.messageUtf8,
|
||||
];
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
if (!(typeof args[0] === "string") || (args[0] as string) != "chat")
|
||||
return undefined;
|
||||
let ev = new ChatBoxEvent();
|
||||
ev.username = args[1] as string;
|
||||
ev.message = args[2] as string;
|
||||
ev.uuid = args[3] as string;
|
||||
ev.isHidden = args[4] as boolean;
|
||||
ev.messageUtf8 = args[5] as string;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
let eventInitializers: ((args: any[]) => IEvent | null)[] = [
|
||||
CharEvent.init,
|
||||
KeyEvent.init,
|
||||
PasteEvent.init,
|
||||
TimerEvent.init,
|
||||
TaskCompleteEvent.init,
|
||||
RedstoneEvent.init,
|
||||
TerminateEvent.init,
|
||||
DiskEvent.init,
|
||||
PeripheralEvent.init,
|
||||
RednetMessageEvent.init,
|
||||
ModemMessageEvent.init,
|
||||
HTTPEvent.init,
|
||||
WebSocketEvent.init,
|
||||
MouseEvent.init,
|
||||
ResizeEvent.init,
|
||||
TurtleInventoryEvent.init,
|
||||
SpeakerAudioEmptyEvent.init,
|
||||
ComputerCommandEvent.init,
|
||||
GenericEvent.init
|
||||
export class GenericEvent implements IEvent {
|
||||
public args: any[] = [];
|
||||
public get_name() {
|
||||
return this.args[0] as string;
|
||||
}
|
||||
public get_args() {
|
||||
return this.args.slice(1);
|
||||
}
|
||||
public static init(args: any[]): IEvent | undefined {
|
||||
let ev = new GenericEvent();
|
||||
ev.args = args;
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
let eventInitializers: ((args: unknown[]) => IEvent | undefined)[] = [
|
||||
(args) => CharEvent.init(args),
|
||||
(args) => KeyEvent.init(args),
|
||||
(args) => PasteEvent.init(args),
|
||||
(args) => TimerEvent.init(args),
|
||||
(args) => TaskCompleteEvent.init(args),
|
||||
(args) => RedstoneEvent.init(args),
|
||||
(args) => TerminateEvent.init(args),
|
||||
(args) => DiskEvent.init(args),
|
||||
(args) => PeripheralEvent.init(args),
|
||||
(args) => RednetMessageEvent.init(args),
|
||||
(args) => ModemMessageEvent.init(args),
|
||||
(args) => HTTPEvent.init(args),
|
||||
(args) => WebSocketEvent.init(args),
|
||||
(args) => MouseEvent.init(args),
|
||||
(args) => ResizeEvent.init(args),
|
||||
(args) => TurtleInventoryEvent.init(args),
|
||||
(args) => SpeakerAudioEmptyEvent.init(args),
|
||||
(args) => ComputerCommandEvent.init(args),
|
||||
(args) => ChatBoxEvent.init(args),
|
||||
(args) => GenericEvent.init(args),
|
||||
];
|
||||
|
||||
type Constructor<T extends {} = {}> = new (...args: any[]) => T;
|
||||
export function pullEventRaw(filter: string | null = null): IEvent | null {
|
||||
let args = table.pack(...coroutine.yield(filter));
|
||||
for (let init of eventInitializers) {
|
||||
let ev = init(args);
|
||||
if (ev != null) return ev;
|
||||
}
|
||||
return GenericEvent.init(args);
|
||||
export function pullEventRaw(
|
||||
filter: string | undefined = undefined,
|
||||
): IEvent | undefined {
|
||||
let args = table.pack(...coroutine.yield(filter));
|
||||
for (let init of eventInitializers) {
|
||||
let ev = init(args);
|
||||
if (ev != undefined) return ev;
|
||||
}
|
||||
return GenericEvent.init(args);
|
||||
}
|
||||
export function pullEvent(filter: string | null = null): IEvent | null {
|
||||
let ev = pullEventRaw(filter);
|
||||
if (ev instanceof TerminateEvent) throw "Terminated";
|
||||
return ev;
|
||||
export function pullEvent(
|
||||
filter: string | undefined = undefined,
|
||||
): IEvent | undefined {
|
||||
let ev = pullEventRaw(filter);
|
||||
if (ev instanceof TerminateEvent) throw "Terminated";
|
||||
return ev;
|
||||
}
|
||||
export function pullEventRawAs<T extends IEvent>(type: Constructor<T>, filter: string | null = null): T | null {
|
||||
let ev = pullEventRaw(filter);
|
||||
if ((ev instanceof type)) return ev as T;
|
||||
else return null;
|
||||
export function pullEventRawAs<T extends IEvent>(
|
||||
type: Constructor<T>,
|
||||
filter: string | undefined = undefined,
|
||||
): T | undefined {
|
||||
let ev = pullEventRaw(filter);
|
||||
if (ev instanceof type) return ev as T;
|
||||
else return undefined;
|
||||
}
|
||||
export function pullEventAs<T extends IEvent>(type: Constructor<T>, filter: string | null = null): T | null {
|
||||
let ev = pullEvent(filter);
|
||||
if ((ev instanceof type)) return ev as T;
|
||||
else return null;
|
||||
export function pullEventAs<T extends IEvent>(
|
||||
type: Constructor<T>,
|
||||
filter: string | undefined = undefined,
|
||||
): T | undefined {
|
||||
let ev = pullEvent(filter);
|
||||
if (ev instanceof type) return ev as T;
|
||||
else return undefined;
|
||||
}
|
||||
|
||||
3
src/test/main.ts
Normal file
3
src/test/main.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { testTimeBasedRotation } from "./testCcLog";
|
||||
|
||||
testTimeBasedRotation();
|
||||
35
src/test/testCcLog.ts
Normal file
35
src/test/testCcLog.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { CCLog, MINUTE, HOUR, SECOND } from "@/lib/ccLog";
|
||||
|
||||
// Test the new time-based rotation functionality
|
||||
function testTimeBasedRotation() {
|
||||
print("Testing time-based log rotation functionality...");
|
||||
|
||||
// Test with default interval (1 day)
|
||||
const logger1 = new CCLog("test_log_default.txt");
|
||||
logger1.info("This is a test message with default interval (1 day)");
|
||||
|
||||
// Test with custom interval (1 hour)
|
||||
const logger2 = new CCLog("test_log_hourly.txt", HOUR);
|
||||
logger2.info("This is a test message with 1-hour interval");
|
||||
|
||||
// Test with custom interval (30 minutes)
|
||||
const logger3 = new CCLog("test_log_30min.txt", 30 * MINUTE);
|
||||
logger3.info("This is a test message with 30-minute interval");
|
||||
|
||||
// Test with custom interval (5 seconds)
|
||||
const logger4 = new CCLog("test_log_5sec.txt", 5 * SECOND);
|
||||
logger4.info("This is a test message with 5-second interval");
|
||||
for (let i = 0; i < 10; i++) {
|
||||
logger4.info(`This is a test message with 5-second interval ${i}`);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
logger1.close();
|
||||
logger2.close();
|
||||
logger3.close();
|
||||
logger4.close();
|
||||
|
||||
print("Test completed successfully!");
|
||||
}
|
||||
|
||||
export { testTimeBasedRotation };
|
||||
Reference in New Issue
Block a user