Files
Construct-CN/Construct[BP]/scripts/lib/canopy/CanopyExtension.js
T
2025-04-21 13:22:19 -07:00

166 lines
6.3 KiB
JavaScript

/**
* @license
* MIT License
*
* Copyright (c) 2024 ForestOfLight
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { world } from '@minecraft/server';
import IPC from '../ipc/ipc';
import Command from './Command';
import Rule from './Rule';
import { CommandCallbackRequest, CommandPrefixRequest, Ready, RegisterCommand, RegisterExtension, RegisterRule, RuleValueRequest, RuleValueSet, CommandPrefixResponse, RuleValueResponse } from './extension.ipc';
class CanopyExtension {
name;
version;
author;
description;
#commands = {};
#rules = {};
#isRegistrationReady = false;
constructor({ name = 'Unnamed', version = '1.0.0', author = 'Unknown', description = { text: '' } }) {
this.id = this.#makeID(name);
this.name = name;
this.version = version;
this.author = author;
this.description = description;
this.#registerExtension();
this.#setupCommandPrefix();
this.#handleCommandCallbacks();
this.#handleRuleValueRequests();
this.#handleRuleValueSetters();
}
addCommand(command) {
if (!(command instanceof Command))
throw new Error('Command must be an instance of Command.');
this.#commands[command.getName()] = command;
if (this.#isRegistrationReady)
this.#registerCommand(command);
}
addRule(rule) {
if (!(rule instanceof Rule))
throw new Error('Rule must be an instance of Rule.');
this.#rules[rule.getID()] = rule;
if (this.#isRegistrationReady)
this.#registerRule(rule);
}
getRuleValue(ruleID) {
return this.#rules[ruleID].getValue();
}
#makeID(name) {
if (typeof name !== 'string')
throw new Error(`[${name}] Could not register extension. Extension name must be a string.`);
const id = name.toLowerCase().replace(/[^a-z0-9 ]/g, '').replace(/ /g, '_');
if (id.length === 0)
throw new Error(`[${name}] Could not register extension. Extension name must contain at least one alphanumeric character.`);
return id;
}
#registerExtension() {
IPC.once('canopyExtension:ready', Ready, () => {
IPC.send('canopyExtension:registerExtension', RegisterExtension, {
name: this.name,
version: this.version,
author: this.author,
description: this.description
});
});
IPC.once(`canopyExtension:${this.id}:ready`, Ready, () => {
this.#isRegistrationReady = true;
for (const rule of Object.values(this.#rules))
this.#registerRule(rule);
for (const command of Object.values(this.#commands))
this.#registerCommand(command);
});
}
#registerCommand(command) {
IPC.send(`canopyExtension:${this.id}:registerCommand`, RegisterCommand, {
name: command.getName(),
description: command.getDescription(),
usage: command.getUsage(),
callback: false,
args: command.getArgs(),
contingentRules: command.getContingentRules(),
adminOnly: command.isAdminOnly(),
helpEntries: command.getHelpEntries(),
helpHidden: command.isHelpHidden(),
extensionName: this.name
});
}
#handleCommandCallbacks() {
IPC.on(`canopyExtension:${this.id}:commandCallbackRequest`, CommandCallbackRequest, (cmdData) => {
if (cmdData.senderName === undefined)
return;
const sender = world.getPlayers({ name: cmdData.senderName })[0];
if (!sender)
throw new Error(`Sender ${cmdData.senderName} of ${cmdData.commandName} not found.`);
const parsedArgs = JSON.parse(cmdData.args);
this.#commands[cmdData.commandName].runCallback(sender, parsedArgs);
});
}
#registerRule(rule) {
IPC.send(`canopyExtension:${this.id}:registerRule`, RegisterRule, {
identifier: rule.getID(),
description: rule.getDescription(),
contingentRules: rule.getContigentRules(),
independentRules: rule.getIndependentRules(),
extensionName: this.name
});
if (rule.getValue() === true)
rule.onEnable();
}
#handleRuleValueRequests() {
IPC.handle(`canopyExtension:${this.id}:ruleValueRequest`, RuleValueRequest, RuleValueResponse, (data) => {
const rule = this.#rules[data.ruleID];
if (!rule)
throw new Error(`Rule ${data.ruleID} not found.`);
const value = rule.getValue();
return { value };
});
}
#handleRuleValueSetters() {
IPC.on(`canopyExtension:${this.id}:ruleValueSet`, RuleValueSet, (data) => {
const rule = this.#rules[data.ruleID];
if (!rule)
throw new Error(`Rule ${data.ruleID} not found.`);
rule.setValue(data.value);
});
}
#setupCommandPrefix() {
const prefix = IPC.invoke(`canopyExtension:commandPrefixRequest`, CommandPrefixRequest, void 0, CommandPrefixResponse).then(result => {
Command.setPrefix(result.prefix);
});
}
}
export { CanopyExtension, Command, Rule };