diff --git a/packages/create/create.js b/packages/create/create.js index ebf161e1..66e4084a 100755 --- a/packages/create/create.js +++ b/packages/create/create.js @@ -12,6 +12,7 @@ const util = require("util"); const yargs = require("yargs"); const { levels, logger, setLogLevel } = require("./logging"); +const { copyPluginTemplates } = require("./template"); let dev = false; const scriptExt = process.platform === "win32" ? ".cmd" : ""; const finished = util.promisify(stream.finished); @@ -513,144 +514,6 @@ WantedBy=multi-user.target } } -async function copyPluginTemplateFile(src, dst, props) { - logger.verbose(`Writing ${dst} from template ${src}`); - if (await fs.exists(dst)) { - logger.warn(`Could not create file ${dst} because it already exists`); - return; - } - - const templateData = await fs.readFile(src, "utf8"); - const outputData = templateData - // matches: // [prop] // anything // [] // or /*/ [prop] /*/ anything /*/ [] /*/ - .replace(/\/\*?\/ \[([a-z_]*)\] \/\*?\/(.*?)\/\*?\/ \[\] \/\*?\//gs, (_, prop, content) => { - // Find and replace // [prop] //content// [] // - if (!props.hasOwnProperty(prop)) { - logger.warn(`Unknown property clause (${prop}) found in template file ${src}`); - return content; - } - - // Would be nice to support + (and) | (or) for prop clauses - return Boolean(props[prop]) ? content : ""; - }) - // matches: // prop // - .replace(/\/\/ ([a-z_]*) \/\//g, (_, prop) => { - // Find and replace // prop // - if (!props.hasOwnProperty(prop)) { - logger.warn(`Unknown property string (${prop}) found in template file ${src}`); - return prop; - } - - return props[prop]; - }); - - await fs.outputFile(dst, outputData); -} - -async function copyPluginTemplate(pluginName, templates) { - logger.info(`Please wait, coping templates for ${templates.join(", ")}`); - const devDependencies = new Map(); - const entryPoints = new Map(); - const files = new Map(); - const prepare = []; - - const javascriptOnly = templates.includes("js"); - const webpack = templates.includes("controller") || templates.includes("web"); - - // Get the file extension and path to the templates - const ext = javascriptOnly ? "js" : "ts"; - const templatePath = path.resolve(__dirname, javascriptOnly ? "./templates/plugin-js" : "./templates/plugin-ts"); - - // Files included in all templates - files.set(".gitignore", path.join(templatePath, ".gitignore")); - files.set(".npmignore", path.join(templatePath, ".npmignore")); - files.set("package.json", path.join(templatePath, "package.json")); - files.set(`messages.${ext}`, path.join(templatePath, `messages.${ext}`)); - files.set(`index.${ext}`, path.join(templatePath, `index.${ext}`)); - - // Files and dependencies to support typescript - if (!javascriptOnly) { - if (webpack) { - devDependencies.set("@types/react", "^18.2.21"); - files.set("tsconfig.web.json", path.join(templatePath, "tsconfig.web.json")); - } - devDependencies.set("@types/node", "^20.4.5"); - devDependencies.set("typescript", "^5.1.6"); - files.set("tsconfig.json", path.join(templatePath, "tsconfig.json")); - prepare.push("tsc --build"); - } - - // Files and dependences to support webpack - if (webpack) { - devDependencies.set("webpack", "^5.88.2"); - devDependencies.set("webpack-cli", "^5.1.4"); - devDependencies.set("webpack-merge", "^5.9.0"); - devDependencies.set("@clusterio/web_ui", "^2.0.0-alpha.14"); - entryPoints.set("webEntrypoint", "./web"); - prepare.push("webpack-cli --env production"); - files.set("webpack.config.js", path.join(templatePath, "webpack.config.js")); - files.set(`web/index.${ext}x`, path.join(templatePath, - templates.includes("web") ? `web/plugin.${ext}x` : `web/no_plugin.${ext}x` - )); - } - - if (templates.includes("controller")) { - entryPoints.set("controllerEntrypoint", "dist/plugin/controller"); - files.set(`controller.${ext}`, path.join(templatePath, `controller.${ext}`)); - } - - if (templates.includes("host")) { - entryPoints.set("hostEntrypoint", "dist/plugin/host"); - files.set(`host.${ext}`, path.join(templatePath, `host.${ext}`)); - } - - if (templates.includes("instance")) { - entryPoints.set("instanceEntrypoint", "dist/plugin/instance"); - files.set(`instance.${ext}`, path.join(templatePath, `instance.${ext}`)); - } - - if (templates.includes("module")) { - files.set(".luacheckrc", path.join(templatePath, ".luacheckrc")); - files.set("module/module.json", path.join(templatePath, "module/module.json")); - files.set("module/control.lua", path.join(templatePath, "module/control.lua")); - files.set("module/module_exports.lua", path.join(templatePath, "module/module_exports.lua")); - } - - if (templates.includes("ctl")) { - entryPoints.set("ctlEntrypoint", "dist/plugin/ctl"); - files.set(`ctl.${ext}`, path.join(templatePath, `ctl.${ext}`)); - } - - // Properties that will control the replacements in the templates - const props = { - controller: templates.includes("controller"), - subscribable: templates.includes("controller") && templates.includes("web"), - host: templates.includes("host"), - instance: templates.includes("instance"), - module: templates.includes("module"), - ctl: templates.includes("ctl"), - web: templates.includes("web"), - plugin_name: pluginName, - prepare: prepare - .join(" && "), - entry_points: [...entryPoints.entries()] - .map((entry) => `${entry[0]}: "${entry[1]}",`) - .join("\n\t"), - dev_dependencies: [...devDependencies.entries()] - .map((entry) => `"${entry[0]}": "${entry[1]}",`) - .join("\n\t\t"), - }; - - // Write all the template files - const writes = []; - for (let [dst, src] of files) { - writes.push(copyPluginTemplateFile(src, dst, props)); - } - - await Promise.all(writes); - logger.info("Successfully wrote all template files"); -} - // eslint-disable-next-line complexity async function inquirerMissingArgs(args) { let answers = {}; @@ -972,7 +835,7 @@ async function main() { logger.verbose(JSON.stringify(answers)); if (answers.pluginTemplate) { - await copyPluginTemplate(answers.pluginName, answers.pluginTemplate); + await copyPluginTemplates(answers.pluginName, answers.pluginTemplate); return; } diff --git a/packages/create/template.js b/packages/create/template.js new file mode 100644 index 00000000..8a998169 --- /dev/null +++ b/packages/create/template.js @@ -0,0 +1,165 @@ +"use strict"; +const { logger } = require("./logging"); +const fs = require("fs-extra"); +const path = require("path"); + +async function copyTemplateFile(src, dst, properties) { + logger.verbose(`Writing ${dst} from template ${src}`); + + // Check if the file already exists to prevent regex calculations + if (await fs.exists(dst)) { + logger.warn(`Could not create file ${dst} because it already exists`); + return; + } + + // Closure, get the property value or throw + function getProperty(property) { + if (!properties.hasOwnProperty(property)) { + throw new Error(`Unknown property (${property}) found in template file ${src}`); + } + + return properties[property]; + } + + // Get the boolean conjunction of a set of properties + function propertyConjunction(props) { + for (const property of props) { + if (property.startsWith("!")) { + if (Boolean(getProperty(property.slice(1)))) { + return false; + } + } else if (!Boolean(getProperty(property))) { + return false; + } + } + + return true; + } + + // Process the the template file + const badChars = /[\(\)-+/*<>=]/; + const templateData = await fs.readFile(src, "utf8"); + const outputData = templateData + // match preprocessor comments: //%// + .replace(/^\/\/%\/\/.*\n/gm, "") + // match preprocessor insert: __property__ + .replace(/__([a-zA-Z][a-zA-Z_]*?[a-zA-Z]?)__/gm, (_, property) => getProperty(property)) + // match preprocessor conditionals: //%if [// comment]\n \n //%endif [comment]\n + .replace(/^\/\/%if ([^\n\/]*)(?:\/\/.*?)?\n((?:.|\n)*?)\/\/%endif.*\n/gm, (_, condition, content) => { + let include = false; + const conjunctions = condition.split("|"); + for (const conjunct of conjunctions) { + if (badChars.test(conjunct)) { + throw new Error("Preprocessor condition can only contain logical operators" + + "such as & | or ! it can not have arithmetic, comparisons or parenthesis" + ); + } + + const property_names = conjunct.split("&").map(s => s.trim()); + if (propertyConjunction(property_names)) { + include = true; + break; + } + } + + return include ? content : ""; + }); + + await fs.outputFile(dst, outputData); +} + +async function copyPluginTemplates(pluginName, templates) { + logger.info(`Please wait, coping templates for ${templates.join(", ")}`); + const files = new Map(); + const prepare = []; + + const javascriptOnly = templates.includes("js"); + const webpack = templates.includes("controller") || templates.includes("web"); + + // Get the file extension and path to the templates + const ext = javascriptOnly ? "js" : "ts"; + const templatePath = path.resolve(__dirname, javascriptOnly ? "./templates/plugin-js" : "./templates/plugin-ts"); + const commonPath = path.resolve(__dirname, "./templates/common"); + + // Files included in all templates + files.set(".gitignore", path.join(commonPath, ".gitignore")); + files.set(".npmignore", path.join(commonPath, ".npmignore")); + files.set("package.json", path.join(commonPath, "package.json")); + files.set(`messages.${ext}`, path.join(templatePath, `messages.${ext}`)); + files.set(`index.${ext}`, path.join(templatePath, `index.${ext}`)); + + // Files and dependencies to support typescript + if (!javascriptOnly) { + prepare.push("tsc --build"); + files.set("tsconfig.json", path.join(templatePath, "tsconfig.json")); + files.set("tsconfig.node.json", path.join(templatePath, "tsconfig.node.json")); + if (webpack) { + files.set("tsconfig.browser.json", path.join(templatePath, "tsconfig.browser.json")); + } + } + + // Files and dependences to support webpack + if (webpack) { + prepare.push("webpack-cli --env production"); + files.set("webpack.config.js", path.join(commonPath, "webpack.config.js")); + files.set(`web/index.${ext}x`, path.join(templatePath, + templates.includes("web") ? `web/plugin.${ext}x` : `web/no_plugin.${ext}x` + )); + } + + // Files for the controller + if (templates.includes("controller")) { + files.set(`controller.${ext}`, path.join(templatePath, `controller.${ext}`)); + } + + // Files for hosts + if (templates.includes("host")) { + files.set(`host.${ext}`, path.join(templatePath, `host.${ext}`)); + } + + // Files for instances + if (templates.includes("instance")) { + files.set(`instance.${ext}`, path.join(templatePath, `instance.${ext}`)); + } + + // Files for lua modules + if (templates.includes("module")) { + files.set(".luacheckrc", path.join(commonPath, ".luacheckrc")); + files.set("module/module.json", path.join(commonPath, "module/module.json")); + files.set("module/control.lua", path.join(commonPath, "module/control.lua")); + files.set("module/module_exports.lua", path.join(commonPath, "module/module_exports.lua")); + } + + // Files for the control tool + if (templates.includes("ctl")) { + files.set(`ctl.${ext}`, path.join(templatePath, `ctl.${ext}`)); + } + + // Properties that will control the replacements in the templates + const properties = { + typescript: !javascriptOnly, + controller: templates.includes("controller"), + host: templates.includes("host"), + instance: templates.includes("instance"), + module: templates.includes("module"), + ctl: templates.includes("ctl"), + web: templates.includes("web"), + plugin_name: pluginName, + prepare: prepare.join(" && "), + ext: ext, + }; + + // Write all the template files + const writes = []; + for (let [dst, src] of files) { + writes.push(copyTemplateFile(src, dst, properties)); + } + + // Wait for writes to complete + await Promise.all(writes); + logger.info("Successfully wrote all template files"); +} + +module.exports = { + copyPluginTemplates, +}; diff --git a/packages/create/templates/plugin-js/.gitignore b/packages/create/templates/common/.gitignore similarity index 100% rename from packages/create/templates/plugin-js/.gitignore rename to packages/create/templates/common/.gitignore diff --git a/packages/create/templates/plugin-js/.luacheckrc b/packages/create/templates/common/.luacheckrc similarity index 100% rename from packages/create/templates/plugin-js/.luacheckrc rename to packages/create/templates/common/.luacheckrc diff --git a/packages/create/templates/plugin-js/.npmignore b/packages/create/templates/common/.npmignore similarity index 71% rename from packages/create/templates/plugin-js/.npmignore rename to packages/create/templates/common/.npmignore index ebd99907..fbac3cb2 100644 --- a/packages/create/templates/plugin-js/.npmignore +++ b/packages/create/templates/common/.npmignore @@ -1,2 +1,3 @@ webpack.config.js dist/web/build +dist/browser diff --git a/packages/create/templates/plugin-ts/module/control.lua b/packages/create/templates/common/module/control.lua similarity index 86% rename from packages/create/templates/plugin-ts/module/control.lua rename to packages/create/templates/common/module/control.lua index c472aadd..2747b83b 100644 --- a/packages/create/templates/plugin-ts/module/control.lua +++ b/packages/create/templates/common/module/control.lua @@ -10,12 +10,12 @@ local MyModule = { -- setupGlobalData should either be removed or called during clusterio_api.events.on_server_startup local globalData = {} local function setupGlobalData() - if global["// plugin_name //"] == nil then - global["// plugin_name //"] = { + if global["__plugin_name__"] == nil then + global["__plugin_name__"] = { -- starting values go here } end - globalData = global["// plugin_name //"] + globalData = global["__plugin_name__"] end --- Public methods should be available though your top level module table @@ -36,10 +36,12 @@ end --- Factorio events are accessible through defines.events, you can have one handler per event per module MyModule.events[defines.events.on_player_crafted_item] = function(event) - game.print(game.table_to_json(event))// [instance] // - clusterio_api.send_json("// plugin_name //-plugin_example_ipc", { + game.print(game.table_to_json(event)) +//%if instance + clusterio_api.send_json("__plugin_name__-plugin_example_ipc", { tick = game.tick, player_name = game.get_player(event.player_index).name - })// [] // + }) +//%endif end --- Nth tick is a special case that requires its own table, the index represents the time period between calls in ticks diff --git a/packages/create/templates/common/module/module.json b/packages/create/templates/common/module/module.json new file mode 100644 index 00000000..60a1ddbe --- /dev/null +++ b/packages/create/templates/common/module/module.json @@ -0,0 +1,14 @@ +{ + "name": "__plugin_name__", + "load": [ + "control.lua" + ], + "require": [ +//%if instance + "module_exports.lua" +//%endif + ], + "dependencies": { + "clusterio": "*" + } +} diff --git a/packages/create/templates/plugin-ts/module/module_exports.lua b/packages/create/templates/common/module/module_exports.lua similarity index 62% rename from packages/create/templates/plugin-ts/module/module_exports.lua rename to packages/create/templates/common/module/module_exports.lua index ecce6291..6235edab 100644 --- a/packages/create/templates/plugin-ts/module/module_exports.lua +++ b/packages/create/templates/common/module/module_exports.lua @@ -1,8 +1,10 @@ -local MyModule = require("./control")// [instance] // +local MyModule = require("./control") +//%if instance --- Can contain anything you plan to use with send_rcon including functions, modules, or data --- luacheck: globals ipc_// plugin_name // -ipc_// plugin_name // = MyModule// [] // +-- luacheck: globals ipc___plugin_name__ +ipc___plugin_name__ = MyModule +//%endif --- Can contain anything you want to allow other plugins to have access to, this example exposes the whole module return MyModule diff --git a/packages/create/templates/common/package.json b/packages/create/templates/common/package.json new file mode 100644 index 00000000..fc2ee57e --- /dev/null +++ b/packages/create/templates/common/package.json @@ -0,0 +1,53 @@ +{ + "name": "__plugin_name__", + "version": "0.1.0", + "description": "", +//%if typescript + "main": "dist/node/index.js", +//%endif +//%if !typescript + "main": "index.js", +//%endif + "scripts": { + "prepare": "__prepare__" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@clusterio/lib": "^2.0.0-alpha.17" + }, + "devDependencies": { +//%// Typescript and type declarations +//%if typescript + "typescript": "^5.1.6", + "@types/node": "^20.4.5", +//%endif +//%if typescript & web + "@types/react": "^18.2.21", +//%endif +//%// Everything else +//%if web + "antd": "^5.13.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", +//%endif +//%if web | controller // The controller uses webpack even when there is no plugin code + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4", + "webpack-merge": "^5.9.0", + "@clusterio/web_ui": "^2.0.0-alpha.17", +//%endif + "@clusterio/lib": "^2.0.0-alpha.17" + }, + "dependencies": { + "@sinclair/typebox": "^0.30.4" + }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "clusterio", + "factorio" + ] +} diff --git a/packages/create/templates/plugin-js/webpack.config.js b/packages/create/templates/common/webpack.config.js similarity index 63% rename from packages/create/templates/plugin-js/webpack.config.js rename to packages/create/templates/common/webpack.config.js index 3d43cfbd..bda4c382 100644 --- a/packages/create/templates/plugin-js/webpack.config.js +++ b/packages/create/templates/common/webpack.config.js @@ -7,21 +7,29 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.__ext__x", output: { path: path.resolve(__dirname, "dist", "web"), }, plugins: [ new webpack.container.ModuleFederationPlugin({ - name: "// plugin_name //", - library: { type: "var", name: "plugin_// plugin_name //" }, + name: "__plugin_name__", + library: { type: "var", name: "plugin___plugin_name__" }, exposes: { - "./": "./info.js", + "./": "./index.__ext__", "./package.json": "./package.json", +//%if web + "./web": "./web/index.__ext__x", +//%endif }, shared: { "@clusterio/lib": { import: false }, "@clusterio/web_ui": { import: false }, +//%if web + "antd": { import: false }, + "react": { import: false }, + "react-dom": { import: false }, +//%endif }, }), ], diff --git a/packages/create/templates/plugin-js/controller.js b/packages/create/templates/plugin-js/controller.js index 7d566479..266e06fa 100644 --- a/packages/create/templates/plugin-js/controller.js +++ b/packages/create/templates/plugin-js/controller.js @@ -1,26 +1,38 @@ "use strict"; const lib = require("@clusterio/lib"); const { BaseControllerPlugin } = require("@clusterio/controller"); -const { PluginExampleEvent, PluginExampleRequest,/*/ [subscribable] /*/ ExampleSubscribableUpdate, ExampleSubscribableValue/*/ [] /*/ } = require("./messages"); -class ControllerPlugin extends BaseControllerPlugin {// [subscribable] // +const { + PluginExampleEvent, PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + ExampleSubscribableUpdate, ExampleSubscribableValue, +//%endif +} = require("./messages"); + +class ControllerPlugin extends BaseControllerPlugin { +//%if controller & web // Subscribing requires web content and the controller exampleDatabase; storageDirty = false; - // [] // + +//%endif async init() { this.controller.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); - this.controller.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));// [subscribable] // + this.controller.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); +//%if controller & web // Subscribing requires web content and the controller this.controller.subscriptions.handle(ExampleSubscribableUpdate, this.handleExampleSubscription.bind(this)); - this.exampleDatabase = new Map(); // If needed, replace with loading from database file // [] // + this.exampleDatabase = new Map(); // If needed, replace with loading from database file +//%endif } async onControllerConfigFieldChanged(field, curr, prev) { this.logger.info(`controller::onControllerConfigFieldChanged ${field}`); - }// [instance] // + } +//%if instance async onInstanceConfigFieldChanged(instance, field, curr, prev) { this.logger.info(`controller::onInstanceConfigFieldChanged ${instance.id} ${field}`); - }// [] // + } +//%endif async onSaveData() { this.logger.info("controller::onSaveData"); @@ -44,14 +56,16 @@ class ControllerPlugin extends BaseControllerPlugin {// [subscribable] // myResponseString: request.myString, myResponseNumbers: request.myNumberArray, }; - }// [subscribable] // + } +//%if controller & web // Subscribing requires web content and the controller async handleExampleSubscription(request) { const values = [...this.exampleDatabase.values()].filter( value => value.updatedAtMs > request.lastRequestTimeMs, ); return values.length ? new ExampleSubscribableUpdate(values) : null; - }// [] // + } +//%endif } module.exports = { diff --git a/packages/create/templates/plugin-js/ctl.js b/packages/create/templates/plugin-js/ctl.js index 5140697c..ed1d1a32 100644 --- a/packages/create/templates/plugin-js/ctl.js +++ b/packages/create/templates/plugin-js/ctl.js @@ -5,7 +5,7 @@ const { PluginExampleEvent, PluginExampleRequest } = require("./messages"); /* eslint-disable no-console */ const pluginCommands = new CommandTree({ - name: "// plugin_name //", description: "The default description I forgot to change", + name: "__plugin_name__", description: "The default description I forgot to change", }); pluginCommands.add(new Command({ diff --git a/packages/create/templates/plugin-js/index.js b/packages/create/templates/plugin-js/index.js index 32ca9d87..035988ce 100644 --- a/packages/create/templates/plugin-js/index.js +++ b/packages/create/templates/plugin-js/index.js @@ -1,79 +1,102 @@ "use strict"; const lib = require("@clusterio/lib"); -const messages = require("./messages"); +const Messages = require("./messages"); lib.definePermission({ - name: "// plugin_name //.example.permission.event", + name: "__plugin_name__.example.permission.event", title: "Example permission event", description: "My plugin's example permission that I forgot to remove", }); lib.definePermission({ - name: "// plugin_name //.example.permission.request", + name: "__plugin_name__.example.permission.request", title: "Example permission request", description: "My plugin's example permission that I forgot to remove", -});// [subscribable] // +}); +//%if controller & web // Subscribing requires web content and the controller lib.definePermission({ - name: "// plugin_name //.example.permission.subscribe", + name: "__plugin_name__.example.permission.subscribe", title: "Example permission subscribe", description: "My plugin's example permission that I forgot to remove", -});// [] //// [web] // +}); +//%endif +//%if web lib.definePermission({ - name: "// plugin_name //.page.view", + name: "__plugin_name__.page.view", title: "Example page view permission", description: "My plugin's example page permission that I forgot to remove", -});// [] // +}); +//%endif const plugin = { name: "// plugin_name //", title: "// plugin_name //", - description: "I didn't update my description",// [entry_points] // - // entry_points // -// [] //// [controller] // + description: "I didn't update my description", +//%if controller + + controllerEntrypoint: "./dist/node/controller", controllerConfigFields: { - "// plugin_name //.myControllerField": { + "__plugin_name__.myControllerField": { title: "My Controller Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [host] // + }, +//%endif +//%if host + + hostEntrypoint: "./dist/node/host", hostConfigFields: { - "// plugin_name //.myHostField": { + "__plugin_name__.myHostField": { title: "My Host Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [instance] // + }, +//%endif +//%if instance + + instanceEntrypoint: "./dist/node/instance", instanceConfigFields: { - "// plugin_name //.myInstanceField": { + "__plugin_name__.myInstanceField": { title: "My Instance Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [ctl] // + }, +//%endif +//%if ctl + + ctlEntrypoint: "./dist/node/ctl", controlConfigFields: { - "// plugin_name //.myControlField": { + "__plugin_name__.myControlField": { title: "My Control Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] // + }, +//%endif messages: [ - messages.PluginExampleEvent, - messages.PluginExampleRequest,// [subscribable] // - messages.ExampleSubscribableUpdate,// [] // + Messages.PluginExampleEvent, + Messages.PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + Messages.ExampleSubscribableUpdate, +//%endif ], +//%if controller | web // The controller always includes web entry even if there is no content + + webEntrypoint: "./web", + routes: [], +//%endif }; module.exports = { plugin, - PluginExampleEvent, - PluginExampleRequest, }; diff --git a/packages/create/templates/plugin-js/instance.js b/packages/create/templates/plugin-js/instance.js index 99a6f19a..a62f5520 100644 --- a/packages/create/templates/plugin-js/instance.js +++ b/packages/create/templates/plugin-js/instance.js @@ -6,8 +6,10 @@ const { PluginExampleEvent, PluginExampleRequest } = require("./messages"); class InstancePlugin extends BaseInstancePlugin { async init() { this.instance.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); - this.instance.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));// [module] // - this.instance.server.handle("ipc-// plugin_name //-plugin_example_ipc", this.handlePluginExampleIPC.bind(this));// [] // + this.instance.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); +//%if module + this.instance.server.handle("ipc-__plugin_name__-plugin_example_ipc", this.handlePluginExampleIPC.bind(this)); +//%endif } async onInstanceConfigFieldChanged(field, curr, prev) { @@ -27,8 +29,10 @@ class InstancePlugin extends BaseInstancePlugin { } async handlePluginExampleEvent(event) { - this.logger.info(JSON.stringify(event));// [module] // - this.sendRcon("/sc ipc_// plugin_name //.foo()");// [] // + this.logger.info(JSON.stringify(event)); +//%if module + this.sendRcon("/sc ipc___plugin_name__.foo()"); +//%endif } async handlePluginExampleRequest(request) { @@ -37,11 +41,13 @@ class InstancePlugin extends BaseInstancePlugin { myResponseString: request.myString, myResponseNumbers: request.myNumberArray, }; - }// [module] // + } +//%if module async handlePluginExampleIPC(event) { this.logger.info(JSON.stringify(event)); - }// [] // + } +//%endif } module.exports = { diff --git a/packages/create/templates/plugin-js/messages.js b/packages/create/templates/plugin-js/messages.js index b6659ce4..ac982267 100644 --- a/packages/create/templates/plugin-js/messages.js +++ b/packages/create/templates/plugin-js/messages.js @@ -6,8 +6,8 @@ class PluginExampleEvent { static type = "event"; static src = ["host", "control"]; static dst = ["controller", "host", "instance"]; - static plugin = "// plugin_name //"; - static permission = "// plugin_name //.example.permission.event"; + static plugin = "__plugin_name__"; + static permission = "__plugin_name__.example.permission.event"; constructor(myString, myNumberArray) { this.myString = myString; @@ -28,8 +28,8 @@ class PluginExampleRequest { static type = "request"; static src = ["host", "control"]; static dst = ["controller", "host", "instance"]; - static plugin = "// plugin_name //"; - static permission = "// plugin_name //.example.permission.request"; + static plugin = "__plugin_name__"; + static permission = "__plugin_name__.example.permission.request"; constructor(myString, myNumberArray) { this.myString = myString; @@ -49,7 +49,8 @@ class PluginExampleRequest { "myResponseString": Type.String(), "myResponseNumbers": Type.Array(Type.Number()), })); -}// [subscribable] // +} +//%if controller & web // Subscribing requires web content and the controller class ExampleSubscribableValue { constructor(id, updatedAtMs, isDeleted) { @@ -73,8 +74,8 @@ class ExampleSubscribableUpdate { static type = "event"; static src = "controller"; static dst = "control"; - static plugin = "// plugin_name //"; - static permission = "// plugin_name //.example.permission.subscribe"; + static plugin = "__plugin_name__"; + static permission = "__plugin_name__.example.permission.subscribe"; constructor(updates) { this.updates = updates; @@ -87,11 +88,14 @@ class ExampleSubscribableUpdate { static fromJSON(json) { return new this(json.updates.map(update => ExampleSubscribableValue.fromJSON(update))); } -}// [] // +} +//%endif module.exports = { PluginExampleEvent, - PluginExampleRequest,// [subscribable] // + PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller ExampleSubscribableValue, - ExampleSubscribableUpdate,// [] // + ExampleSubscribableUpdate, +//%endif }; diff --git a/packages/create/templates/plugin-js/module/control.lua b/packages/create/templates/plugin-js/module/control.lua deleted file mode 100644 index c472aadd..00000000 --- a/packages/create/templates/plugin-js/module/control.lua +++ /dev/null @@ -1,52 +0,0 @@ -local clusterio_api = require("modules/clusterio/api") - ---- Top level module table, contains event handlers and public methods -local MyModule = { - events = {}, - on_nth_tick = {}, -} - ---- global is 'synced' between players, you should use your plugin name to avoid conflicts --- setupGlobalData should either be removed or called during clusterio_api.events.on_server_startup -local globalData = {} -local function setupGlobalData() - if global["// plugin_name //"] == nil then - global["// plugin_name //"] = { - -- starting values go here - } - end - globalData = global["// plugin_name //"] -end - ---- Public methods should be available though your top level module table -function MyModule.foo() - game.print("foo") -end - ---- Private methods should be local to the file, this will prevent others from calling it -local function bar() - game.print("bar") -end - ---- Clusterio provides a few custom events, on_server_startup is the most useful and should be used in place of on_load -MyModule.events[clusterio_api.events.on_server_startup] = function(event) - setupGlobalData() - game.print(game.table_to_json(event)) -end - ---- Factorio events are accessible through defines.events, you can have one handler per event per module -MyModule.events[defines.events.on_player_crafted_item] = function(event) - game.print(game.table_to_json(event))// [instance] // - clusterio_api.send_json("// plugin_name //-plugin_example_ipc", { - tick = game.tick, player_name = game.get_player(event.player_index).name - })// [] // -end - ---- Nth tick is a special case that requires its own table, the index represents the time period between calls in ticks -MyModule.on_nth_tick[300] = function() - game.print(game.tick) - bar() -end - ---- Always return the top level module table from control, this is how clusterio will access your event handlers -return MyModule diff --git a/packages/create/templates/plugin-js/module/module.json b/packages/create/templates/plugin-js/module/module.json deleted file mode 100644 index 33915ac9..00000000 --- a/packages/create/templates/plugin-js/module/module.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "// plugin_name //", - "load": [ - "control.lua" - ], - "require": [ - // [instance] //"module_exports.lua"// [] // - ], - "dependencies": [ - - ] -} diff --git a/packages/create/templates/plugin-js/module/module_exports.lua b/packages/create/templates/plugin-js/module/module_exports.lua deleted file mode 100644 index ecce6291..00000000 --- a/packages/create/templates/plugin-js/module/module_exports.lua +++ /dev/null @@ -1,8 +0,0 @@ -local MyModule = require("./control")// [instance] // - ---- Can contain anything you plan to use with send_rcon including functions, modules, or data --- luacheck: globals ipc_// plugin_name // -ipc_// plugin_name // = MyModule// [] // - ---- Can contain anything you want to allow other plugins to have access to, this example exposes the whole module -return MyModule diff --git a/packages/create/templates/plugin-js/package.json b/packages/create/templates/plugin-js/package.json deleted file mode 100644 index 627f1431..00000000 --- a/packages/create/templates/plugin-js/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "// plugin_name //", - "version": "0.1.0", - "description": "", - "main": "index.js", - "scripts": { - "prepare": "// prepare //" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@clusterio/lib": "^2.0.0-alpha.14" - }, - "devDependencies": { - // dev_dependencies // - "@clusterio/lib": "^2.0.0-alpha.14" - }, - "dependencies": { - "@sinclair/typebox": "^0.30.4" - }, - "publishConfig": { - "access": "public" - }, - "keywords": [ - "clusterio", - "factorio" - ] -} diff --git a/packages/create/templates/plugin-js/web/plugin.jsx b/packages/create/templates/plugin-js/web/plugin.jsx index 5c6784ba..db095219 100644 --- a/packages/create/templates/plugin-js/web/plugin.jsx +++ b/packages/create/templates/plugin-js/web/plugin.jsx @@ -1,46 +1,68 @@ -const { useContext, useEffect, useState/*/ [subscribable] /*/, useCallback, useSyncExternalStore/*/ [] /*/ } = require("react"); -// const { } = require("antd"); +import React, { + useContext, useEffect, useState, +//%if controller & web // Subscribing requires web content and the controller + useCallback, useSyncExternalStore, +//%endif +} from "react"; -const { +// import { +// +// } from "antd"; + +import { BaseWebPlugin, PageLayout, Control, ControlContext, notifyErrorHandler, -} = require("@clusterio/web_ui"); +} from "@clusterio/web_ui"; -const lib = require("@clusterio/lib"); -const { PluginExampleEvent, PluginExampleRequest/*/ [subscribable] /*/, ExampleSubscribableUpdate/*/ [] /*/ } = require("../messages"); +import { + PluginExampleEvent, PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + ExampleSubscribableUpdate, +//%endif +} from "../messages"; + +import * as lib from "@clusterio/lib"; function MyTemplatePage() { - let control = useContext(ControlContext);// [subscribable] // - const plugin = control.plugins.get("// plugin_name //"); - const [subscribableData, synced] = plugin.useSubscribableData();// [] // + let control = useContext(ControlContext); +//%if controller & web // Subscribing requires web content and the controller + const plugin = control.plugins.get("__plugin_name__"); + const [subscribableData, synced] = plugin.useSubscribableData(); +//%endif - return -

// plugin_name //

// [subscribable] // - Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])}// [] // + return +

__plugin_name__

+//%if controller & web // Subscribing requires web content and the controller + Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])} +//%endif
; } -class WebPlugin extends BaseWebPlugin {// [subscribable] // +class WebPlugin extends BaseWebPlugin { +//%if controller & web // Subscribing requires web content and the controller subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control); - // [] // + +//%endif async init() { this.pages = [ { - path: "/// plugin_name //", - sidebarName: "// plugin_name //", - permission: "// plugin_name //.page.view", + path: "/__plugin_name__", + sidebarName: "__plugin_name__", + permission: "__plugin_name__.page.view", content: , }, ]; this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); - }// [subscribable] // + } +//%if controller & web // Subscribing requires web content and the controller useSubscribableData() { const control = useContext(ControlContext); const subscribe = useCallback((callback) => this.subscribableData.subscribe(callback), [control]); return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot()); - }// [] // + } +//%endif async handlePluginExampleEvent(event) { this.logger.info(JSON.stringify(event)); diff --git a/packages/create/templates/plugin-ts/.gitignore b/packages/create/templates/plugin-ts/.gitignore deleted file mode 100644 index 1a993212..00000000 --- a/packages/create/templates/plugin-ts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -dist/ -node_modules/ -package-lock.json diff --git a/packages/create/templates/plugin-ts/.luacheckrc b/packages/create/templates/plugin-ts/.luacheckrc deleted file mode 100644 index cacf598d..00000000 --- a/packages/create/templates/plugin-ts/.luacheckrc +++ /dev/null @@ -1,1705 +0,0 @@ ---[[---------------------------------------------------------------------------- - LICENSE - .luacheckrc for Factorio Version 0.18.8, luacheck version 0.23.0 - This file is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this file, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this file dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE FILE 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 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. - - For more information, please refer to -------------------------------------------------------------------------------]] -local LINE_LENGTH = false - -local IGNORE = {'21./%w+_$', '21./^_%w+$', '213/[ijk]', '213/index', '213/key'} - --- These globals are not available to the factorio API -local NOT_GLOBALS = {'coroutine', 'io', 'socket', 'dofile', 'loadfile'} - -local STD_CONTROL = 'lua52+factorio+factorio_control+stdlib+factorio_defines' -local STD_DATA = 'lua52+factorio+factorio_data+stdlib+stdlib_data+factorio_defines' - --- For Base and Core Mods -local STD_BASE_DATA = 'lua52+factorio+factorio_data+factorio_defines+factorio_base_data' -local STD_BASE_CONTROL = 'lua52+factorio+factorio_control+factorio_defines+factorio_base_control' - -do -- Assume Factorio Control Stage as Default - std = STD_CONTROL - --cache = true - not_globals = NOT_GLOBALS - ignore = IGNORE - quiet = 1 -- pass -q option - max_cyclomatic_complexity = false - codes = true - max_line_length = LINE_LENGTH - max_code_line_length = LINE_LENGTH - max_string_line_length = LINE_LENGTH - max_comment_line_length = LINE_LENGTH - - --List of files and directories to exclude - exclude_files = { - --Ignore special folders - '**/.trash/', - '**/.history/', - '**/stdlib/vendor/', - - --Ignore development mods - '**/combat-tester/', - '**/test-maker/', - '**/trailer/', - - --Ignore love Includes - '**/love/includes/' - } -end - -do -- RedMew and ExpGaming overrides - globals = { - 'math', 'table', 'package', - 'print', 'require', 'unpack', 'inspect', 'loadstring', 'ServerCommands', 'Debug', - '_C', '_DEBUG', '_CHEATS', '_DUMP_ENV', '_LIFECYCLE', '_STAGE', - } -end - -do -- Set default prototype files - files['**/data.lua'].std = STD_DATA - files['**/data-updates.lua'].std = STD_DATA - files['**/data-final-fixes.lua'].std = STD_DATA - files['**/settings.lua'].std = STD_DATA - files['**/settings-updates.lua'].std = STD_DATA - files['**/settings-final-fixes.lua'].std = STD_DATA - files['**/prototypes/'].std = STD_DATA - files['**/settings/'].std = STD_DATA -end - -do -- Base and Core mod files - local base_scenarios = { - std = STD_BASE_CONTROL .. '+factorio_base_scenarios+factorio_base_story', - --ignore = {'212/event', '111', '112', '113', '211', '212', '213', '311', '411', '412', '421', '422', '423', '431', '432', '512'} - ignore = {'...'} - } - files['**/base/scenarios/'] = base_scenarios - files['**/base/tutorials/'] = base_scenarios - files['**/base/campaigns/'] = base_scenarios - files['**/wip-scenario/'] = base_scenarios - - files['**/base/migrations/'] = {std = STD_BASE_CONTROL} - - files['**/core/lualib/'] = {std = STD_BASE_CONTROL} - files['**/core/lualib/util.lua'] = {globals = {'util', 'table'}, ignore = {'432/object'}} - files['**/core/lualib/silo-script.lua'] = {globals = {'silo_script'}, ignore = {'4../player'}} - files['**/core/lualib/production-score.lua'] = {globals = {'production_score', 'get_price_recursive'}, ignore = {'4../player'}} - files['**/core/lualib/story*'] = {std = '+factorio_base_story', ignore = {'42./k', '42./filter'}} - files['**/core/lualib/mod-gui.lua'] = {globals = {'mod_gui'}} - files['**/core/lualib/camera.lua'] = {globals = {'camera'}} - files['**/core/lualib/builder.lua'] = {globals = {'Builder', 'builder', 'action', 'down', 'right'}} - - files['**/core/lualib/bonus-gui-ordering/'] = {std = STD_BASE_DATA} - files['**/core/lualib/dataloader.lua'] = {globals = {'data'}} - files['**/core/lualib/circuit-connector-*'] = {std = STD_BASE_DATA..'+factorio_circuit_connector_generated'} - files['**/core/lualib/bonus-gui-ordering.lua'] = {globals = {'bonus_gui_ordering'}} - - files['**/base/prototypes/'] = {std = STD_BASE_DATA} - files['**/core/prototypes/'] = {std = STD_BASE_DATA} - files['**/core/prototypes/noise-programs.lua'] = {ignore = {'212/x', '212/y', '212/tile', '212/map'}} -end - -do -- Stdlib Files - local stdlib_control = { - std = 'lua52+factorio+factorio_control+stdlib+factorio_defines', - max_line_length = LINE_LENGTH - } - - local stdlib_data = { - std = 'lua52+factorio+factorio_data+stdlib+factorio_defines', - max_line_length = LINE_LENGTH - } - - -- Assume control stage for stdlib - files['**/stdlib/'] = stdlib_control - - -- Assume generic lua for stdlib utils - files['**/stdlib/utils/**'].std = 'lua52+stdlib' - - -- STDLIB data stage files - files['**/stdlib/data/'] = stdlib_data - - -- STDLIB Busted Spec - files['**/spec/**'] = { - globals = {'serpent', 'log', 'package.remove_stdlib'}, - std = 'lua52c+busted+factorio_defines+factorio_control+stdlib' - } - - -- Love - files['**/love/'].std = 'luajit+love+love_extra+stdlib+stdlib_data' -end - -do -- Factorio STDs-- - stds.factorio = { - --Set the read only variables - read_globals = { - -- @log@: Gives writing access to Factorio's logger instance. - "log", - -- @serpent@: Lua serializer and pretty printer. (https://github.com/pkulchenko/serpent). - "serpent", - -- @table_size@: Returns the number of elements inside an LUA table. - "table_size", - -- @lldebugger@: Provided by lua local debugger vscode extension. - lldebugger = { - fields = {'requestBreak'}, - other_fields = true, - read_only = false, - }, - -- @__DebugAdapter@: Provided by Factorio Mod Debug vscode extension. - __DebugAdapter = { - fields = {'print', 'stepIgnoreAll', 'stepIgnore', 'breakpoint'}, - other_fields = true, - read_only = false - }, - util = { - fields = { - "by_pixel", "distance", "findfirstentity", "positiontostr", "formattime", "moveposition", "oppositedirection", - "ismoduleavailable", "multiplystripes", "format_number", "increment", "color", "conditional_return", - "add_shift", "merge", "premul_color", "encode", "decode", "insert_safe", - table = { - fields = { - "compare", "deepcopy" - }, - }, - }, - }, - table = { - fields = { - "compare", "deepcopy" - }, - }, - }, - } - - stds.factorio_control = { - read_globals = { - - -- @commands@: - commands = { - fields = { - "add_command", "commands", "game_commands", "remove_command" - }, - }, - - -- @settings@: - settings = { - fields = { - "get_player_settings", - startup = {other_fields = true}, - global = {other_fields = true}, - player = {other_fields = true}, - get = {read_only = false}, -- stdlib added - get_startup = {read_only = false} -- stdlib added - }, - }, - - -- @script@: Provides an interface for registering event handlers. - -- (http://lua-api.factorio.com/latest/LuaBootstrap.html) - script = { - fields = { - "on_event", "on_nth_tick", "on_configuration_changed", "on_init", "on_load", "generate_event_name", - "raise_event", "get_event_handler", "mod_name", "get_event_order", - "is_game_in_debug_mode", "object_name", "set_event_filter", "get_event_filter", "register_metatable", - active_mods = {read_only = true, other_fields = true}, - }, - other_fields = false, - }, - - -- @remote@: Allows inter-mod communication by providing a repository of interfaces that is shared by all mods. - -- (http://lua-api.factorio.com/latest/LuaRemote.html) - remote = { - fields = { - interfaces = {read_only = false, other_fields = true}, - "add_interface", "remove_interface", "call" - }, - read_only = true, - other_fields = false, - }, - - rcon = { - fields = {'print'} - }, - - rendering = { - other_fields = false, - read_only = true, - fields = { - 'draw_animation', - 'draw_line', - 'draw_text', - 'draw_circle', - 'draw_rectangle', - 'draw_arc', - 'draw_polygon', - 'draw_sprite', - 'draw_light', - 'destroy', - 'is_font_valid', - 'is_valid', - 'get_all_ids', - 'clear', - 'get_type', - 'get_surface', - 'get_time_to_live', - 'set_time_to_live', - 'get_forces', - 'set_forces', - 'get_players', - 'set_players', - 'get_color', - 'set_color', - 'get_width', - 'set_width', - 'get_from', - 'set_from', - 'get_to', - 'set_to', - 'get_gap_amount', - 'set_gap_amount', - 'get_gap_length', - 'set_gap_length', - 'get_target', - 'set_target', - 'get_orientation', - 'set_orientation', - 'get_scale', - 'set_scale', - 'get_text', - 'set_text', - 'get_font', - 'set_font', - 'get_alignment', - 'set_alignment', - 'get_scale_with_zoom', - 'set_scale_with_zoom', - 'get_filled', - 'set_filled', - 'get_radius', - 'set_radius', - 'get_left_top', - 'set_left_top', - 'get_right_bottom', - 'set_right_bottom', - 'get_max_radius', - 'set_max_radius', - 'get_min_radius', - 'set_min_radius', - 'get_start_angle', - 'set_start_angle', - 'get_angle', - 'set_angle', - 'get_vertices', - 'set_vertices', - 'get_sprite', - 'set_sprite', - 'get_x_scale', - 'set_x_scale', - 'get_y_scale', - 'set_y_scale', - 'get_render_layer', - 'set_render_layer', - 'get_orientation_target', - 'set_orientation_target', - 'get_oriented_offset', - 'set_oriented_offset', - 'get_intensity', - 'set_intensity', - 'get_minimum_darkness', - 'set_minimum_darkness' - } - }, - - -- @game@: Main object through which most of the API is accessed. - -- It is, however, not available inside handlers registered with @script.on_load@. - -- (http://lua-api.factorio.com/latest/LuaGameScript.html) - game ={ - other_fields = false, - read_only = false, - fields = { - 'auto_save', - 'ban_player', - 'check_consistency', - 'check_prototype_translations', - 'count_pipe_groups', - 'create_force', - 'create_profiler', - 'create_random_generator', - 'create_surface', - 'decode_string', - 'delete_surface', - 'direction_to_string', - 'disable_replay', - 'disable_tips_and_tricks', - 'disable_tutorial_triggers', - 'encode_string', - 'evaluate_expression', - 'force_crc', - 'get_active_entities_count', - 'get_entity_by_tag', - 'get_filtered_achievement_prototypes', - 'get_filtered_decorative_prototypes', - 'get_filtered_entity_prototypes', - 'get_filtered_equipment_prototypes', - 'get_filtered_fluid_prototypes', - 'get_filtered_item_prototypes', - 'get_filtered_mod_setting_prototypes', - 'get_filtered_recipe_prototypes', - 'get_filtered_technology_prototypes', - 'get_filtered_tile_prototypes', - 'get_map_exchange_string', - 'get_player', - 'get_surface', - 'get_train_stops', - 'help', - 'is_demo', - 'is_multiplayer', - 'is_valid_sound_path', - 'is_valid_sprite_path', - 'json_to_table', - 'kick_player', - 'log_recipe_locale', - 'merge_forces', - 'mute_player', - 'parse_map_exchange_string', - 'play_sound', - 'print', - 'print_stack_size', - 'purge_player', - 'regenerate_entity', - 'reload_mods', - 'reload_script', - 'remove_offline_players', - 'remove_path', - 'reset_time_played', - 'save_atlas', - 'server_save', - 'set_game_state', - 'set_wait_for_screenshots_to_finish', - 'show_message_dialog', - 'table_to_json', - 'take_screenshot', - 'take_technology_screenshot', - 'unban_player', - 'unmute_player', - 'write_file', - - achievement_prototypes = {read_only = true, other_fields = true}, - active_mods = {read_only = true, other_fields = true}, - ammo_category_prototypes = {read_only = true, other_fields = true}, - autoplace_control_prototypes = {read_only = true, other_fields = true}, - backer_names = {read_only = true, other_fields = true}, - connected_players = {read_only = true, other_fields = true}, - custom_input_prototypes = {read_only = true, other_fields = true}, - damage_prototypes = {read_only = true, other_fields = true}, - decorative_prototypes = {read_only = true, other_fields = true}, - default_map_gen_settings = {read_only = true, other_fields = true}, - difficulty = {read_only = true, other_fields = true}, - difficulty_settings = {read_only = true, other_fields = true}, - entity_prototypes = {read_only = true, other_fields = true}, - equipment_category_prototypes = {read_only = true, other_fields = true}, - equipment_grid_prototypes = {read_only = true, other_fields = true}, - equipment_prototypes = {read_only = true, other_fields = true}, - finished = {read_only = true, other_fields = true}, - fluid_prototypes = {read_only = true, other_fields = true}, - forces = {read_only = true, other_fields = true}, - fuel_category_prototypes = {read_only = true, other_fields = true}, - item_group_prototypes = {read_only = true, other_fields = true}, - item_prototypes = {read_only = true, other_fields = true}, - item_subgroup_prototypes = {read_only = true, other_fields = true}, - map_settings = {read_only = true, other_fields = true}, - mod_setting_prototypes = {read_only = true, other_fields = true}, - module_category_prototypes = {read_only = true, other_fields = true}, - named_noise_expressions = {read_only = true, other_fields = true}, - noise_layer_prototypes = {read_only = true, other_fields = true}, - object_name = {read_only = true, other_fields = true}, - particle_prototypes = {read_only = true, other_fields = true}, - permissions = {read_only = true, other_fields = true}, - player = {read_only = true, other_fields = true}, - players = {read_only = true, other_fields = true}, - pollution_statistics = {read_only = true, other_fields = true}, - recipe_category_prototypes = {read_only = true, other_fields = true}, - recipe_prototypes = {read_only = true, other_fields = true}, - resource_category_prototypes = {read_only = true, other_fields = true}, - shortcut_prototypes = {read_only = true, other_fields = true}, - styles = {read_only = true, other_fields = true}, - surfaces = {read_only = true, other_fields = true}, - technology_prototypes = {read_only = true, other_fields = true}, - tick = {read_only = true, other_fields = true}, - ticks_played = {read_only = true, other_fields = true}, - tile_prototypes = {read_only = true, other_fields = true}, - trivial_smoke_prototypes = {read_only = true, other_fields = true}, - virtual_signal_prototypes = {read_only = true, other_fields = true}, - - autosave_enabled = {read_only = false, other_fields = false}, - draw_resource_selection = {read_only = false, other_fields = false}, - enemy_has_vision_on_land_mines = {read_only = false, other_fields = false}, - speed = {read_only = false, other_fields = false}, - tick_paused = {read_only = false, other_fields = false}, - ticks_to_run = {read_only = false, other_fields = false} - }, - }, - }, - - globals = { - -- @global@: The global dictionary, useful for storing data persistent across a save-load cycle. - -- Writing access is given to the mod-id field (for mod-wise saved data). - -- (http://lua-api.factorio.com/latest/Global.html) - "global", - - -- @MOD@: Keep it organized, use this variable for anything that "NEEDS" to be global for some reason. - "MOD" - }, - } - - stds.factorio_data = { - - read_globals = { - data = { - fields = { - raw = { - other_fields = true, - read_only = false - }, - "extend", "is_demo" - }, - }, - - settings = { - fields = { - "startup", "global", "player", "get", "get_startup" - }, - }, - - mods = { - other_fields = true - }, - - -- Popular mods that think they need globals! - angelsmods = { - other_fields = true - }, - - -- Popular mods that think they need globals! - bobmods = { - other_fields = true - }, - - - } - } -end - -do -- Factorio Base/Core STDs-- - stds.factorio_base_control = { - read_globals = {"silo_script", "mod_gui", "camera"} - } - - stds.factorio_base_scenarios = { - globals = { - "check_automate_science_packs_advice", "check_research_hints", "check_supplies", "manage_attacks", "all_dead", - "on_win", "difficulty_number", "init_attack_data", "handle_attacks", "count_items_in_container", "progress", "scanned", - "check_light", "check_machine_gun", "level", "story_table", - - "tightspot_prices", "tightspot_make_offer", "tightspot_init", "tightspot_get_required_balance", - "tightspot_init_level", "tightspot_init_spending_frame", "tightspot_init_progress_frame", "tightspot_update_progress", "tightspot_update_spending", - "tightspot_get_missing_to_win", "tightspot_sell_back", "tightspot_start_level", "tightspot_show_level_description", "tightspot_update_speed_label", - "map_ignore", "tightspot_check_level", "land_price", - - "transport_belt_madness_init", "transport_belt_madness_init_level", "transport_belt_madness_create_chests", "transport_belt_madness_fill_chests", - "transport_belt_madness_start_level", "map_ignore", "map_clear", "map_load", "map_save", "transport_belt_madness_show_level_description", - "transport_belt_madness_check_level", "transport_belt_madness_next_level", "transport_belt_madness_clear_level", "transport_belt_madness_contains_next_level", - - "restricted", "check_built_items", "result", "disable_combat_technologies", "apply_character_modifiers", "apply_combat_modifiers", "apply_balance", - "load_config", "starting_area_constant", "create_next_surface", "end_round", "prepare_next_round", "silo_died","choose_joining_gui", - "destroy_joining_guis", "create_random_join_gui", "create_auto_assign_gui", "create_pick_join_gui", "create_config_gui", "make_config_table", "default", - "make_team_gui", "make_team_gui_config", "add_team_button_press", "trash_team_button_press", "remove_team_from_team_table", "add_team_to_team_table", - "set_teams_from_gui", "on_team_button_press", "make_color_dropdown", "create_balance_option", "create_disable_frame", "disable_frame", "parse_disabled_items", - "set_balance_settings", "config_confirm", "parse_config_from_gui", "get_color", "roll_starting_area", "delete_roll_surfaces", "auto_assign", - "destroy_config_for_all", "prepare_map", "set_evolution_factor", "update_players_on_team_count", "random_join", "init_player_gui", - "destroy_player_gui", "objective_button_press", "admin_button_press", "admin_frame_button_press", "diplomacy_button_press", "update_diplomacy_frame", - "diplomacy_frame_button_press", "team_changed_diplomacy", "diplomacy_check_press", "get_stance", "give_inventory", "setup_teams", "disable_items_for_all", - "set_random_team", "set_diplomacy", "create_spawn_positions", "set_spawn_position", "set_team_together_spawns", "chart_starting_area_for_force_spawns", - "check_starting_area_chunks_are_generated", "check_player_color", "check_round_start", "clear_starting_area_enemies", "check_no_rush_end", "check_no_rush_players", - "finish_setup", "chart_area_for_force", "setup_start_area_copy", "update_copy_progress", "update_progress_bar", "copy_paste_starting_area_tiles", - "copy_paste_starting_area_entities", "create_silo_for_force", "setup_research", "on_chunk_generated", "get_distance_to_nearest_spawn", - "create_wall_for_force", "fpn", "give_items", "create_item_frame", "create_technologies_frame", "create_cheat_frame", "create_day_frame", - "time_modifier", "points_per_second_start", "points_per_second_level_subtract", "levels", "update_info", "get_time_left", "update_time_left", - "on_joined", "make_frame", "update_frame", "update_table", "calculate_task_item_multiplayer", "setup_config", "select_from_probability_table", - "select_inventory", "select_equipment", "select_challange_type", "save_round_statistics", "start_challenge", "create_teams", "set_areas", - "decide_player_team", "set_teams", "refresh_leaderboard", "set_player", "generate_technology_list", "generate_research_task","setup_unlocks", - "check_technology_progress", "generate_production_task", "generate_shopping_list_task", "set_gui_flow_table", "create_visibility_button", - "check_item_lists", "update_task_gui", "check_end_of_round", "end_round_gui_update", "try_to_check_victory", "update_gui", "check_start_round", - "check_start_set_areas", "check_start_setting_entities", "check_set_areas", "check_clear_areas", "check_chests", "check_chests_shopping_list", - "check_chests_production", "check_input_chests", "fill_input_chests", "check_victory", "shopping_task_finished", "calculate_force_points", - "update_research_task_table", "update_production_task_table", "update_shopping_list_task_table", "create_joined_game_gui", "pre_ending_round", - "player_ending_prompt", "update_end_timer", "update_begin_timer", "team_finished", "save_points_list", "give_force_players_points", - "update_winners_list", "set_spectator", "set_character", "give_starting_inventory", "give_equipment", "shuffle_table", "format_time", - "spairs", "fill_leaderboard", "create_grid", "simple_entities", "save_map_data", "clear_map", "create_tiles", "recreate_entities", - "map_sets", "give_points", "init_forces", "init_globals", "init_unit_settings", "check_next_wave", "next_wave", "calculate_wave_power", - "wave_end", "make_next_spawn_tick", "check_spawn_units", "get_wave_units", "spawn_units", "randomize_ore", "set_command", "command_straglers", - "unit_config", "make_next_wave_tick", "time_to_next_wave", "time_to_wave_end", "rocket_died", "unit_died", "get_bounty_price", "setup_waypoints", - "insert_items", "give_starting_equipment", "give_spawn_equipment", "next_round_button_visible", "gui_init", "create_wave_frame", "create_money_frame", - "create_upgrade_gui", "update_upgrade_listing", "upgrade_research", "get_upgrades", "get_money", "update_connected_players", "update_round_number", - "set_research", "set_recipes", "check_deconstruction", "check_blueprint_placement", "loop_entities", "experiment_items", - "setup", "story_gui_click", "clear_surface", "add_run_trains_button", "puzzle_condition", "basic_signals", - "loop_trains", "Y_offset", "ghosts_1", "ghosts_2", "required_path", "through_wall_path", "count", "check_built_real_rail", - "current_ghosts_count", "other", "rails", "set_rails", "straight_section", "late_entities", "entities", "stop", - "get_spawn_coordinate", - - --tutorials - "intermission", "create_entities_on_tick", "on_player_created", "required_count", "non_player_entities", "clear_rails", - "chest", "damage", "furnace", "init_prototypes", "build_infi_table", "junk", "update_player_tags", "time_left", "team_production", - "create_task_frame", "create_visibilty_buttons", "update_leaderboard", "in_in_area" - } - } - - stds.factorio_base_data = { - globals = { - --Style - "make_cursor_box", "make_full_cursor_box", - "default_container_padding", "default_orange_color", "default_light_orange_color", "warning_red_color", - "achievement_green_color", "achievement_tan_color", "orangebuttongraphcialset", "bluebuttongraphcialset", - "bonus_gui_ordering", "trivial_smoke", "technology_slot_base_width", "technology_slot_base_height", "default_frame_font_vertical_compensation", - - --Belts - "transport_belt_connector_frame_sprites", "transport_belt_circuit_wire_connection_point", "transport_belt_circuit_wire_max_distance", - "transport_belt_circuit_connector_sprites", "ending_patch_prototype", "basic_belt_horizontal", "basic_belt_vertical", - "basic_belt_ending_top", "basic_belt_ending_bottom", "basic_belt_ending_side", "basic_belt_starting_top", "basic_belt_starting_bottom", - "basic_belt_starting_side", "fast_belt_horizontal", "fast_belt_vertical", "fast_belt_ending_top", "fast_belt_ending_bottom", - "fast_belt_ending_side", "fast_belt_starting_top", "fast_belt_starting_bottom", "fast_belt_starting_side", "express_belt_horizontal", - "express_belt_vertical", "express_belt_ending_top", "express_belt_ending_bottom", "express_belt_ending_side", "express_belt_starting_top", - "express_belt_starting_bottom", "express_belt_starting_side", - - --Circuit Connectors - "circuit_connector_definitions", "default_circuit_wire_max_distance", "inserter_circuit_wire_max_distance", - "universal_connector_template", "belt_connector_template", "belt_frame_connector_template", "inserter_connector_template", - - --Inserter Circuit Connectors - "inserter_circuit_wire_max_distance", "inserter_default_stack_control_input_signal", - - --Sounds/beams - "make_heavy_gunshot_sounds", "make_light_gunshot_sounds", "make_laser_sounds", - - --Gun/Laser - "gun_turret_extension", "gun_turret_extension_shadow", "gun_turret_extension_mask", "gun_turret_attack", - "laser_turret_extension", "laser_turret_extension_shadow", "laser_turret_extension_mask", - - --Pipes - "pipecoverspictures", "pipepictures", "assembler2pipepictures", "assembler3pipepictures", "make_heat_pipe_pictures", - - --Combinators - "generate_arithmetic_combinator", "generate_decider_combinator", "generate_constant_combinator", - - --Rail - "destroyed_rail_pictures", "rail_pictures", "rail_pictures_internal", "standard_train_wheels", "drive_over_tie", - "rolling_stock_back_light", "rolling_stock_stand_by_light", - - --Enemies - "make_enemy_autoplace", "make_enemy_spawner_autoplace", "make_enemy_worm_autoplace", - "make_spitter_attack_animation", "make_spitter_run_animation", "make_spitter_dying_animation", - "make_spitter_attack_parameters", "make_spitter_roars", "make_spitter_dying_sounds", - "make_spawner_idle_animation", "make_spawner_die_animation", - "make_biter_run_animation", "make_biter_attack_animation", "make_biter_die_animation", - "make_biter_roars", "make_biter_dying_sounds", "make_biter_calls", - "make_worm_roars", "make_worm_dying_sounds", "make_worm_folded_animation", "make_worm_preparing_animation", - "make_worm_prepared_animation", "make_worm_attack_animation", "make_worm_die_animation", - - --Other - "tile_variations_template", "make_water_autoplace_settings", - "make_unit_melee_ammo_type", "make_trivial_smoke", "make_4way_animation_from_spritesheet", "flying_robot_sounds", - "productivitymodulelimitation", "crash_trigger", "capsule_smoke", "make_beam", "playeranimations", - "make_blood_tint", "make_shadow_tint", - - --tiles - "water_transition_template", "make_water_transition_template", "water_autoplace_settings", "water_tile_type_names", - "patch_for_inner_corner_of_transition_between_transition", - } - } - - stds.factorio_base_story = { - globals = { - "story_init_helpers", "story_update_table", "story_init", "story_update", "story_on_tick", "story_add_update", - "story_remove_update", "story_jump_to", "story_elapsed", "story_elapsed_check", "story_show_message_dialog", - "set_goal", "player_set_goal", "on_player_joined", "flash_goal", "set_info", "player_set_info", "export_entities", - "list", "recreate_entities", "entity_to_connect", "limit_camera", "find_gui_recursive", "enable_entity_export", - "add_button", "on_gui_click", "set_continue_button_style", "add_message_log", "story_add_message_log", - "player_add_message_log", "message_log_frame", "message_log_scrollpane", "message_log_close_button", - "message_log_table", "toggle_message_log_button", "toggle_objective_button", "message_log_init", - "add_gui_recursive", "add_toggle_message_log_button", "add_toggle_objective_button", "mod_gui", - "flash_message_log_button", "flash_message_log_on_tick", "story_gui_click", "story_points_by_name", "story_branches", - "player", "surface", "deconstruct_on_tick", "recreate_entities_on_tick", "flying_congrats", "story_table" - } - } - - stds.factorio_circuit_connector_generated = { - globals = { - 'default_circuit_wire_max_distance', 'circuit_connector_definitions', 'universal_connector_template', - 'belt_connector_template', 'belt_frame_connector_template', 'inserter_connector_template', 'inserter_connector_template', - 'inserter_circuit_wire_max_distance', 'inserter_default_stack_control_input_signal', 'transport_belt_connector_frame_sprites', - 'transport_belt_circuit_wire_max_distance', - } - } -end - -do -- STDLIB STDs-- - stds.stdlib = { - read_globals = {}, - globals = { - 'STDLIB', - 'prequire', - 'rawtostring', - 'traceback', - 'data_traceback', - 'inspect', - 'serpent', - 'inline_if', - 'install', - 'log', - 'concat', - 'GAME', - 'AREA', - 'POSITION', - 'TILE', - 'SURFACE', - 'CHUNK', - 'COLOR', - 'ENTITY', - 'INVENTORY', - 'RESOURCE', - 'CONFIG', - 'LOGGER', - 'QUEUE', - 'EVENT', - 'GUI', - 'PLAYER', - 'FORCE', - 'MATH', - 'STRING', - 'TABLE' - } - } - - stds.stdlib_control = {} - - stds.stdlib_data = { - globals = { - 'DATA', - 'RECIPE', - 'ITEM', - 'FLUID', - 'ENTITY', - 'TECHNOLOGY', - 'CATEGORY' - } - } -end - -do -- Love STDs - stds.love_extra = { - read_globals = { - love = { - fields = { - arg = { - fields = { - 'parseGameArguments', - 'parseOption', - 'getLow', - 'optionIndices', - 'options' - } - } - } - }, - 'coroutine', - 'io', - 'socket', - 'dofile', - 'loadfile' - }, - globals = { - love = { - fields = { - 'handlers' - } - } - } - } -end - -do -- Factorio Defines STDs-- - stds.factorio_defines = { - read_globals = { - defines = { - fields = { - alert_type = { - fields = { - 'entity_destroyed', - 'entity_under_attack', - 'not_enough_construction_robots', - 'no_material_for_construction', - 'not_enough_repair_packs', - 'turret_fire', - 'custom', - 'no_storage', - 'train_out_of_fuel', - 'fluid_mixing' - } - }, - behavior_result = { - fields = { - 'in_progress', - 'fail', - 'success', - 'deleted' - } - }, - build_check_type = { - fields = { - 'script', - 'manual', - 'ghost_place', - 'ghost_revive' - } - }, - chain_signal_state = { - fields = { - 'all_open', - 'partially_open', - 'none_open' - } - }, - chunk_generated_status = { - fields = { - 'nothing', - 'custom_tiles', - 'basic_tiles', - 'corrected_tiles', - 'tiles', - 'entities' - } - }, - circuit_condition_index = { - fields = { - 'inserter_circuit', - 'inserter_logistic', - 'lamp', - 'arithmetic_combinator', - 'decider_combinator', - 'constant_combinator', - 'offshore_pump', - 'pump' - } - }, - circuit_connector_id = { - fields = { - 'accumulator', - 'constant_combinator', - 'container', - 'programmable_speaker', - 'rail_signal', - 'rail_chain_signal', - 'roboport', - 'storage_tank', - 'wall', - 'electric_pole', - 'inserter', - 'lamp', - 'combinator_input', - 'combinator_output', - 'offshore_pump', - 'pump' - } - }, - command = { - fields = { - 'attack', - 'go_to_location', - 'compound', - 'group', - 'attack_area', - 'wander', - 'flee', - 'stop', - 'build_base' - } - }, - compound_command = { - fields = { - 'logical_and', - 'logical_or', - 'return_last' - } - }, - control_behavior = { - fields = { - inserter = { - fields = { - circuit_mode_of_operation = { - fields = { - 'enable_disable', - 'set_filters', - 'read_hand_contents', - 'set_stack_size' - } - }, - hand_read_mode = { - fields = { - 'hold', - 'pulse' - } - } - } - }, - lamp = { - fields = { - circuit_mode_of_operation = { - fields = { - 'use_colors' - } - } - } - }, - logistic_container = { - fields = { - circuit_mode_of_operation = { - fields = { - 'send_contents', - 'set_requests' - } - } - } - }, - mining_drill = { - fields = { - resource_read_mode = { - fields = { - 'this_miner', - 'entire_patch' - } - } - } - }, - roboport = { - fields = { - circuit_mode_of_operation = { - fields = { - 'read_logistics', - 'read_robot_stats' - } - } - } - }, - train_stop = { - fields = { - circuit_mode_of_operation = { - fields = { - 'enable_disable', - 'send_to_train', - 'read_from_train', - 'read_stopped_train' - } - } - } - }, - transport_belt = { - fields = { - content_read_mode = { - fields = { - 'pulse', - 'hold' - } - } - } - }, - type = { - fields = { - 'container', - 'generic_on_off', - 'inserter', - 'lamp', - 'logistic_container', - 'roboport', - 'storage_tank', - 'train_stop', - 'decider_combinator', - 'arithmetic_combinator', - 'constant_combinator', - 'transport_belt', - 'accumulator', - 'rail_signal', - 'rail_chain_signal', - 'wall', - 'mining_drill', - 'programmable_speaker' - } - } - } - }, - controllers = { - fields = { - 'ghost', - 'character', - 'god', - 'editor', - 'cutscene', - 'spectator' - } - }, - deconstruction_item = { - fields = { - entity_filter_mode = { - fields = { - 'whitelist', - 'blacklist' - } - }, - tile_filter_mode = { - fields = { - 'whitelist', - 'blacklist' - } - }, - tile_selection_mode = { - fields = { - 'normal', - 'always', - 'never', - 'only' - } - } - } - }, - difficulty = { - fields = { - 'easy', - 'normal', - 'hard' - } - }, - difficulty_settings = { - fields = { - recipe_difficulty = { - fields = { - 'normal', - 'expensive' - } - }, - technology_difficulty = { - fields = { - 'normal', - 'expensive' - } - } - } - }, - direction = { - fields = { - 'north', - 'northeast', - 'east', - 'southeast', - 'south', - 'southwest', - 'west', - 'northwest' - } - }, - distraction = { - fields = { - 'by_enemy', - 'by_anything', - 'by_damage' - } - }, - entity_status = { - fields = { - 'working', - 'no_power', - 'no_fuel', - 'no_recipe', - 'no_input_fluid', - 'no_research_in_progress', - 'no_minable_resources', - 'low_input_fluid', - 'low_power', - 'disabled_by_control_behavior', - 'disabled_by_script', - 'fluid_ingredient_shortage', - 'fluid_production_overload', - 'item_ingredient_shortage', - 'item_production_overload', - 'marked_for_deconstruction', - 'missing_required_fluid', - 'missing_science_packs', - 'waiting_for_source_items', - 'waiting_for_space_in_destination', - 'waiting_to_launch_rocket' - } - }, - events = { - fields = { - 'on_tick', - 'on_gui_click', - 'on_gui_confirmed', - 'on_gui_text_changed', - 'on_gui_checked_state_changed', - 'on_entity_died', - 'on_post_entity_died', - 'on_entity_damaged', - 'on_picked_up_item', - 'on_built_entity', - 'on_sector_scanned', - 'on_player_mined_item', - 'on_put_item', - 'on_rocket_launched', - 'on_pre_player_mined_item', - 'on_chunk_generated', - 'on_player_crafted_item', - 'on_robot_built_entity', - 'on_robot_pre_mined', - 'on_robot_mined', - 'on_research_started', - 'on_research_finished', - 'on_player_rotated_entity', - 'on_player_set_quickbar_slot', - 'on_marked_for_deconstruction', - 'on_cancelled_deconstruction', - 'on_trigger_created_entity', - 'on_trigger_fired_artillery', - 'on_train_changed_state', - 'on_player_created', - 'on_resource_depleted', - 'on_player_driving_changed_state', - 'on_force_created', - 'on_forces_merging', - 'on_player_cursor_stack_changed', - 'on_pre_entity_settings_pasted', - 'on_entity_settings_pasted', - 'on_player_main_inventory_changed', - 'on_player_armor_inventory_changed', - 'on_player_ammo_inventory_changed', - 'on_player_gun_inventory_changed', - 'on_player_placed_equipment', - 'on_player_removed_equipment', - 'on_pre_player_died', - 'on_player_died', - 'on_player_respawned', - 'on_player_joined_game', - 'on_player_left_game', - 'on_player_built_tile', - 'on_player_mined_tile', - 'on_robot_built_tile', - 'on_robot_mined_tile', - 'on_player_selected_area', - 'on_player_alt_selected_area', - 'on_player_changed_surface', - 'on_selected_entity_changed', - 'on_market_item_purchased', - 'on_player_dropped_item', - 'on_biter_base_built', - 'on_player_changed_force', - 'on_entity_renamed', - 'on_gui_selection_state_changed', - 'on_runtime_mod_setting_changed', - 'on_difficulty_settings_changed', - 'on_surface_created', - 'on_surface_deleted', - 'on_pre_surface_deleted', - 'on_player_mined_entity', - 'on_robot_mined_entity', - 'on_train_created', - 'on_gui_elem_changed', - 'on_player_setup_blueprint', - 'on_player_deconstructed_area', - 'on_player_configured_blueprint', - 'on_console_chat', - 'on_console_command', - 'on_player_removed', - 'on_pre_player_removed', - 'on_player_used_capsule', - 'script_raised_built', - 'script_raised_destroy', - 'script_raised_revive', - 'on_player_promoted', - 'on_player_demoted', - 'on_combat_robot_expired', - 'on_player_changed_position', - 'on_mod_item_opened', - 'on_gui_opened', - 'on_gui_closed', - 'on_gui_value_changed', - 'on_player_muted', - 'on_player_unmuted', - 'on_player_cheat_mode_enabled', - 'on_player_cheat_mode_disabled', - 'on_character_corpse_expired', - 'on_pre_ghost_deconstructed', - 'on_player_pipette', - 'on_player_display_resolution_changed', - 'on_player_display_scale_changed', - 'on_pre_player_crafted_item', - 'on_player_cancelled_crafting', - 'on_chunk_charted', - 'on_technology_effects_reset', - 'on_land_mine_armed', - 'on_forces_merged', - 'on_player_trash_inventory_changed', - 'on_pre_player_left_game', - 'on_pre_surface_cleared', - 'on_surface_cleared', - 'on_chunk_deleted', - 'on_pre_chunk_deleted', - 'on_train_schedule_changed', - 'on_player_banned', - 'on_player_kicked', - 'on_player_unbanned', - 'on_rocket_launch_ordered', - 'on_script_path_request_finished', - 'on_ai_command_completed', - 'on_marked_for_upgrade', - 'on_cancelled_upgrade', - 'on_player_toggled_map_editor', - 'on_entity_cloned', - 'on_area_cloned', - 'on_brush_cloned', - 'on_game_created_from_scenario', - 'on_surface_imported', - 'on_surface_renamed', - 'on_player_toggled_alt_mode', - 'on_player_repaired_entity', - 'on_player_fast_transferred', - 'on_pre_robot_exploded_cliff', - 'on_robot_exploded_cliff', - 'on_entity_spawned', - 'on_cutscene_waypoint_reached', - 'on_unit_group_created', - 'on_unit_added_to_group', - 'on_unit_removed_from_group', - 'on_unit_group_finished_gathering', - 'on_build_base_arrived', - 'on_chart_tag_added', - 'on_chart_tag_modified', - 'on_chart_tag_removed', - 'on_lua_shortcut', - 'on_gui_location_changed', - 'on_gui_selected_tab_changed', - 'on_gui_switch_state_changed', - 'on_force_cease_fire_changed', - 'on_force_friends_changed', - 'on_string_translated', - 'on_script_trigger_effect' - } - }, - flow_precision_index = { - fields = { - 'one_second', - 'one_minute', - 'ten_minutes', - 'one_hour', - 'ten_hours', - 'fifty_hours', - 'two_hundred_fifty_hours', - 'one_thousand_hours' - } - }, - group_state = { - fields = { - 'gathering', - 'moving', - 'attacking_distraction', - 'attacking_target', - 'finished', - 'pathfinding', - 'wander_in_group' - } - }, - gui_type = { - fields = { - 'entity', - 'research', - 'controller', - 'production', - 'item', - 'bonus', - 'trains', - 'achievement', - 'blueprint_library', - 'equipment', - 'logistic', - 'other_player', - 'kills', - 'permissions', - 'tutorials', - 'custom', - 'server_management', - 'player_management', - 'tile' - } - }, - input_action = { - fields = { - 'activate_copy', - 'activate_cut', - 'activate_paste', - 'add_permission_group', - 'add_train_station', - 'admin_action', - 'alt_select_area', - 'alt_select_blueprint_entities', - 'alternative_copy', - 'begin_mining', - 'begin_mining_terrain', - 'build_item', - 'build_rail', - 'build_terrain', - 'cancel_craft', - 'cancel_deconstruct', - 'cancel_new_blueprint', - 'cancel_research', - 'cancel_upgrade', - 'change_active_item_group_for_crafting', - 'change_active_item_group_for_filters', - 'change_active_quick_bar', - 'change_arithmetic_combinator_parameters', - 'change_blueprint_book_record_label', - 'change_decider_combinator_parameters', - 'change_item_label', - 'change_multiplayer_config', - 'change_picking_state', - 'change_programmable_speaker_alert_parameters', - 'change_programmable_speaker_circuit_parameters', - 'change_programmable_speaker_parameters', - 'change_riding_state', - 'change_shooting_state', - 'change_single_blueprint_record_label', - 'change_train_stop_station', - 'change_train_wait_condition', - 'change_train_wait_condition_data', - 'clean_cursor_stack', - 'clear_selected_blueprint', - 'clear_selected_deconstruction_item', - 'clear_selected_upgrade_item', - 'connect_rolling_stock', - 'copy', - 'copy_entity_settings', - 'craft', - 'create_blueprint_like', - 'cursor_split', - 'cursor_transfer', - 'custom_input', - 'cycle_blueprint_book_backwards', - 'cycle_blueprint_book_forwards', - 'deconstruct', - 'delete_blueprint_library', - 'delete_blueprint_record', - 'delete_custom_tag', - 'delete_permission_group', - 'destroy_opened_item', - 'disconnect_rolling_stock', - 'drag_train_schedule', - 'drag_train_wait_condition', - 'drop_blueprint_record', - 'drop_item', - 'drop_to_blueprint_book', - 'edit_custom_tag', - 'edit_permission_group', - 'export_blueprint', - 'fast_entity_split', - 'fast_entity_transfer', - 'go_to_train_station', - 'grab_blueprint_record', - 'gui_checked_state_changed', - 'gui_click', - 'gui_confirmed', - 'gui_elem_changed', - 'gui_location_changed', - 'gui_selected_tab_changed', - 'gui_selection_state_changed', - 'gui_switch_state_changed', - 'gui_text_changed', - 'gui_value_changed', - 'import_blueprint', - 'import_blueprint_string', - 'import_permissions_string', - 'inventory_split', - 'inventory_transfer', - 'launch_rocket', - 'lua_shortcut', - 'map_editor_action', - 'market_offer', - 'mod_settings_changed', - 'open_achievements_gui', - 'open_blueprint_library_gui', - 'open_blueprint_record', - 'open_bonus_gui', - 'open_character_gui', - 'open_equipment', - 'open_gui', - 'open_item', - 'open_kills_gui', - 'open_logistic_gui', - 'open_mod_item', - 'open_production_gui', - 'open_technology_gui', - 'open_train_gui', - 'open_train_station_gui', - 'open_trains_gui', - 'open_tutorials_gui', - 'paste_entity_settings', - 'place_equipment', - 'quick_bar_pick_slot', - 'quick_bar_set_selected_page', - 'quick_bar_set_slot', - 'remove_cables', - 'remove_train_station', - 'reset_assembling_machine', - 'rotate_entity', - 'select_area', - 'select_blueprint_entities', - 'select_entity_slot', - 'select_item', - 'select_mapper_slot', - 'select_next_valid_gun', - 'select_tile_slot', - 'set_auto_launch_rocket', - 'set_autosort_inventory', - 'set_behavior_mode', - 'set_car_weapons_control', - 'set_circuit_condition', - 'set_circuit_mode_of_operation', - 'set_deconstruction_item_tile_selection_mode', - 'set_deconstruction_item_trees_and_rocks_only', - 'set_entity_color', - 'set_entity_energy_property', - 'set_filter', - 'set_heat_interface_mode', - 'set_heat_interface_temperature', - 'set_infinity_container_filter_item', - 'set_infinity_container_remove_unfiltered_items', - 'set_infinity_pipe_filter', - 'set_inserter_max_stack_size', - 'set_inventory_bar', - 'set_logistic_filter_item', - 'set_logistic_filter_signal', - 'set_logistic_trash_filter_item', - 'set_request_from_buffers', - 'set_research_finished_stops_game', - 'set_signal', - 'set_single_blueprint_record_icon', - 'set_splitter_priority', - 'set_train_stopped', - 'setup_assembling_machine', - 'setup_blueprint', - 'setup_single_blueprint_record', - 'smart_pipette', - 'stack_split', - 'stack_transfer', - 'start_repair', - 'start_research', - 'start_walking', - 'stop_building_by_moving', - 'switch_connect_to_logistic_network', - 'switch_constant_combinator_state', - 'switch_inserter_filter_mode_state', - 'switch_power_switch_state', - 'switch_to_rename_stop_gui', - 'take_equipment', - 'toggle_deconstruction_item_entity_filter_mode', - 'toggle_deconstruction_item_tile_filter_mode', - 'toggle_driving', - 'toggle_enable_vehicle_logistics_while_moving', - 'toggle_equipment_movement_bonus', - 'toggle_map_editor', - 'toggle_personal_roboport', - 'toggle_show_entity_info', - 'translate_string', - 'undo', - 'upgrade', - 'upgrade_opened_blueprint', - 'use_artillery_remote', - 'use_item', - 'wire_dragging', - 'write_to_console' - } - }, - inventory = { - fields = { - 'fuel', - 'burnt_result', - 'chest', - 'furnace_source', - 'furnace_result', - 'furnace_modules', - 'character_main', - 'character_guns', - 'character_ammo', - 'character_armor', - 'character_vehicle', - 'character_trash', - 'god_main', - 'editor_main', - 'editor_guns', - 'editor_ammo', - 'editor_armor', - 'roboport_robot', - 'roboport_material', - 'robot_cargo', - 'robot_repair', - 'assembling_machine_input', - 'assembling_machine_output', - 'assembling_machine_modules', - 'lab_input', - 'lab_modules', - 'mining_drill_modules', - 'item_main', - 'rocket_silo_rocket', - 'rocket_silo_result', - 'rocket', - 'car_trunk', - 'car_ammo', - 'cargo_wagon', - 'turret_ammo', - 'beacon_modules', - 'character_corpse', - 'artillery_turret_ammo', - 'artillery_wagon_ammo' - } - }, - logistic_member_index = { - fields = { - 'logistic_container', - 'vehicle_storage', - 'character_requester', - 'character_storage', - 'character_provider', - 'generic_on_off_behavior' - } - }, - logistic_mode = { - fields = { - 'active_provider', - 'storage', - 'requester', - 'passive_provider', - 'buffer' - } - }, - mouse_button_type = { - fields = { - 'left', - 'right', - 'middle' - } - }, - rail_connection_direction = { - fields = { - 'left', - 'straight', - 'right' - } - }, - rail_direction = { - fields = { - 'front', - 'back' - } - }, - render_mode = { - fields = { - 'game', - 'chart', - 'chart_zoomed_in' - } - }, - rich_text_setting = { - fields = { - 'enabled', - 'disabled', - 'highlight' - } - }, - riding = { - fields = { - acceleration = { - fields = { - 'nothing', - 'accelerating', - 'braking', - 'reversing' - } - }, - direction = { - fields = { - 'left', - 'straight', - 'right' - } - } - } - }, - shooting = { - fields = { - 'not_shooting', - 'shooting_enemies', - 'shooting_selected' - } - }, - signal_state = { - fields = { - 'open', - 'closed', - 'reserved', - 'reserved_by_circuit_network' - } - }, - train_state = { - fields = { - 'on_the_path', - 'path_lost', - 'no_schedule', - 'no_path', - 'arrive_signal', - 'wait_signal', - 'arrive_station', - 'wait_station', - 'manual_control_stop', - 'manual_control' - } - }, - transport_line = { - fields = { - 'left_line', - 'right_line', - 'left_underground_line', - 'right_underground_line', - 'secondary_left_line', - 'secondary_right_line', - 'left_split_line', - 'right_split_line', - 'secondary_left_split_line', - 'secondary_right_split_line' - } - }, - wire_connection_id = { - fields = { - 'electric_pole', - 'power_switch_left', - 'power_switch_right' - } - }, - wire_type = { - fields = { - 'red', - 'green', - 'copper' - } - }, - -- Defines additional modules - color = { - other_fields = true - }, - anticolor = { - other_fields = true - }, - lightcolor = { - other_fields = true - }, - time = { - fields = { - 'second', - 'minute', - 'hour', - 'day', - 'week', - 'month', - 'year' - } - } - } - } - } - } -end - ---[[ Options - "ignore", "std", "globals", "unused_args", "self", "compat", "global", "unused", "redefined", - "unused_secondaries", "allow_defined", "allow_defined_top", "module", - "read_globals", "new_globals", "new_read_globals", "enable", "only", "not_globals", - "max_line_length", "max_code_line_length", "max_string_line_length", "max_comment_line_length", - "max_cyclomatic_complexity" ---]] - ---[[ Warnings list - -- 011 A syntax error. - -- 021 An invalid inline option. - -- 022 An unpaired inline push directive. - -- 023 An unpaired inline pop directive. - -- 111 Setting an undefined global variable. - -- 112 Mutating an undefined global variable. - -- 113 Accessing an undefined global variable. - -- 121 Setting a read-only global variable. - -- 122 Setting a read-only field of a global variable. - -- 131 Unused implicitly defined global variable. - -- 142 Setting an undefined field of a global variable. - -- 143 Accessing an undefined field of a global variable. - -- 211 Unused local variable. - -- 212 Unused argument. - -- 213 Unused loop variable. - -- 221 Local variable is accessed but never set. - -- 231 Local variable is set but never accessed. - -- 232 An argument is set but never accessed. - -- 233 Loop variable is set but never accessed. - -- 241 Local variable is mutated but never accessed. - -- 311 Value assigned to a local variable is unused. - -- 312 Value of an argument is unused. - -- 313 Value of a loop variable is unused. - -- 314 Value of a field in a table literal is unused. - -- 321 Accessing uninitialized local variable. - -- 331 Value assigned to a local variable is mutated but never accessed. - -- 341 Mutating uninitialized local variable. - -- 411 Redefining a local variable. - -- 412 Redefining an argument. - -- 413 Redefining a loop variable. - -- 421 Shadowing a local variable. - -- 422 Shadowing an argument. - -- 423 Shadowing a loop variable. - -- 431 Shadowing an upvalue. - -- 432 Shadowing an upvalue argument. - -- 433 Shadowing an upvalue loop variable. - -- 511 Unreachable code. - -- 512 Loop can be executed at most once. - -- 521 Unused label. - -- 531 Left-hand side of an assignment is too short. - -- 532 Left-hand side of an assignment is too long. - -- 541 An empty do end block. - -- 542 An empty if branch. - -- 551 An empty statement. - -- 611 A line consists of nothing but whitespace. - -- 612 A line contains trailing whitespace. - -- 613 Trailing whitespace in a string. - -- 614 Trailing whitespace in a comment. - -- 621 Inconsistent indentation (SPACE followed by TAB). - -- 631 Line is too long. ---]] diff --git a/packages/create/templates/plugin-ts/.npmignore b/packages/create/templates/plugin-ts/.npmignore deleted file mode 100644 index ebd99907..00000000 --- a/packages/create/templates/plugin-ts/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -webpack.config.js -dist/web/build diff --git a/packages/create/templates/plugin-ts/controller.ts b/packages/create/templates/plugin-ts/controller.ts index b833cc50..cab2d3c0 100644 --- a/packages/create/templates/plugin-ts/controller.ts +++ b/packages/create/templates/plugin-ts/controller.ts @@ -1,25 +1,37 @@ import * as lib from "@clusterio/lib"; import { BaseControllerPlugin, InstanceInfo } from "@clusterio/controller"; -import { PluginExampleEvent, PluginExampleRequest,/*/ [subscribable] /*/ ExampleSubscribableUpdate, ExampleSubscribableValue/*/ [] /*/ } from "./messages"; -export class ControllerPlugin extends BaseControllerPlugin {// [subscribable] // +import { + PluginExampleEvent, PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + ExampleSubscribableUpdate, ExampleSubscribableValue, +//%endif +} from "./messages"; + +export class ControllerPlugin extends BaseControllerPlugin { +//%if controller & web // Subscribing requires web content and the controller exampleDatabase!: Map; storageDirty = false; - // [] // + +//%endif async init() { this.controller.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); - this.controller.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));// [subscribable] // + this.controller.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); +//%if controller & web // Subscribing requires web content and the controller this.controller.subscriptions.handle(ExampleSubscribableUpdate, this.handleExampleSubscription.bind(this)); - this.exampleDatabase = new Map();// [] // + this.exampleDatabase = new Map(); // If needed, replace with loading from database file +//%endif } async onControllerConfigFieldChanged(field: string, curr: unknown, prev: unknown) { this.logger.info(`controller::onControllerConfigFieldChanged ${field}`); - }// [instance] // + } +//%if instance async onInstanceConfigFieldChanged(instance: InstanceInfo, field: string, curr: unknown, prev: unknown) { this.logger.info(`controller::onInstanceConfigFieldChanged ${instance.id} ${field}`); - }// [] // + } +//%endif async onSaveData() { this.logger.info("controller::onSaveData"); @@ -43,12 +55,14 @@ export class ControllerPlugin extends BaseControllerPlugin {// [subscribable] // myResponseString: request.myString, myResponseNumbers: request.myNumberArray, }; - }// [subscribable] // + } +//%if controller & web // Subscribing requires web content and the controller async handleExampleSubscription(request: lib.SubscriptionRequest) { const values = [...this.exampleDatabase.values()].filter( value => value.updatedAtMs > request.lastRequestTimeMs, ); return values.length ? new ExampleSubscribableUpdate(values) : null; - }// [] // + } +//%endif } diff --git a/packages/create/templates/plugin-ts/ctl.ts b/packages/create/templates/plugin-ts/ctl.ts index db6b9c1b..75c3a1a4 100644 --- a/packages/create/templates/plugin-ts/ctl.ts +++ b/packages/create/templates/plugin-ts/ctl.ts @@ -5,7 +5,7 @@ import { PluginExampleEvent, PluginExampleRequest } from "./messages"; /* eslint-disable no-console */ const pluginCommands = new CommandTree({ - name: "// plugin_name //", description: "The default description I forgot to change", + name: "__plugin_name__", description: "The default description I forgot to change", }); pluginCommands.add(new Command({ diff --git a/packages/create/templates/plugin-ts/index.ts b/packages/create/templates/plugin-ts/index.ts index 6db9367c..029a01a7 100644 --- a/packages/create/templates/plugin-ts/index.ts +++ b/packages/create/templates/plugin-ts/index.ts @@ -2,86 +2,119 @@ import * as lib from "@clusterio/lib"; import * as Messages from "./messages"; lib.definePermission({ - name: "// plugin_name //.example.permission.event", + name: "__plugin_name__.example.permission.event", title: "Example permission event", description: "My plugin's example permission that I forgot to remove", }); lib.definePermission({ - name: "// plugin_name //.example.permission.request", + name: "__plugin_name__.example.permission.request", title: "Example permission request", description: "My plugin's example permission that I forgot to remove", -});// [subscribable] // +}); +//%if controller & web // Subscribing requires web content and the controller lib.definePermission({ - name: "// plugin_name //.example.permission.subscribe", + name: "__plugin_name__.example.permission.subscribe", title: "Example permission subscribe", description: "My plugin's example permission that I forgot to remove", -});// [] //// [web] // +}); +//%endif +//%if web lib.definePermission({ - name: "// plugin_name //.page.view", + name: "__plugin_name__.page.view", title: "Example page view permission", description: "My plugin's example page permission that I forgot to remove", -});// [] // +}); +//%endif -declare module "@clusterio/lib" {// [controller] // +declare module "@clusterio/lib" { +//%if controller export interface ControllerConfigFields { - "// plugin_name //.myControllerField": string; - }// [] //// [host] // + "__plugin_name__.myControllerField": string; + } +//%endif +//%if host export interface HostConfigFields { - "// plugin_name //.myHostField": string; - }// [] //// [instance] // + "__plugin_name__.myHostField": string; + } +//%endif +//%if instance export interface InstanceConfigFields { - "// plugin_name //.myInstanceField": string; - }// [] //// [ctl] // + "__plugin_name__.myInstanceField": string; + } +//%endif +//%if ctl export interface ControlConfigFields { - "// plugin_name //.myControlField": string; - }// [] // + "__plugin_name__.myControlField": string; + } +//%endif } export const plugin: lib.PluginDeclaration = { - name: "// plugin_name //", - title: "// plugin_name //", - description: "I didn't update my description",// [entry_points] // - // entry_points // -// [] //// [controller] // + name: "__plugin_name__", + title: "__plugin_name__", + description: "I didn't update my description", +//%if controller + + controllerEntrypoint: "./dist/node/controller", controllerConfigFields: { - "// plugin_name //.myControllerField": { + "__plugin_name__.myControllerField": { title: "My Controller Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [host] // + }, +//%endif +//%if host + + hostEntrypoint: "./dist/node/host", hostConfigFields: { - "// plugin_name //.myHostField": { + "__plugin_name__.myHostField": { title: "My Host Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [instance] // + }, +//%endif +//%if instance + + instanceEntrypoint: "./dist/node/instance", instanceConfigFields: { - "// plugin_name //.myInstanceField": { + "__plugin_name__.myInstanceField": { title: "My Instance Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] //// [ctl] // + }, +//%endif +//%if ctl + + ctlEntrypoint: "./dist/node/ctl", controlConfigFields: { - "// plugin_name //.myControlField": { + "__plugin_name__.myControlField": { title: "My Control Field", description: "This should be removed", type: "string", initialValue: "Remove Me", }, - },// [] // + }, +//%endif messages: [ Messages.PluginExampleEvent, - Messages.PluginExampleRequest,// [subscribable] // - Messages.ExampleSubscribableUpdate,// [] // + Messages.PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + Messages.ExampleSubscribableUpdate, +//%endif ], +//%if controller | web // The controller always includes web entry even if there is no content + + webEntrypoint: "./web", + routes: [], +//%endif }; diff --git a/packages/create/templates/plugin-ts/instance.ts b/packages/create/templates/plugin-ts/instance.ts index 02fce382..d3b8b763 100644 --- a/packages/create/templates/plugin-ts/instance.ts +++ b/packages/create/templates/plugin-ts/instance.ts @@ -1,17 +1,21 @@ import * as lib from "@clusterio/lib"; import { BaseInstancePlugin } from "@clusterio/host"; -import { PluginExampleEvent, PluginExampleRequest } from "./messages";// [module] // +import { PluginExampleEvent, PluginExampleRequest } from "./messages"; +//%if module type PuginExampleIPC = { tick: number, player_name: string, -};// [] // +}; +//%endif export class InstancePlugin extends BaseInstancePlugin { async init() { this.instance.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); - this.instance.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));// [module] // - this.instance.server.handle("ipc-// plugin_name //-plugin_example_ipc", this.handlePluginExampleIPC.bind(this));// [] // + this.instance.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); +//%if module + this.instance.server.handle("ipc-__plugin_name__-plugin_example_ipc", this.handlePluginExampleIPC.bind(this)); +//%endif } async onInstanceConfigFieldChanged(field: string, curr: unknown, prev: unknown) { @@ -27,8 +31,10 @@ export class InstancePlugin extends BaseInstancePlugin { } async onPlayerEvent(event: lib.PlayerEvent) { - this.logger.info(`onPlayerEvent::onPlayerEvent ${JSON.stringify(event)}`);// [module] // - this.sendRcon("/sc ipc_// plugin_name //.foo()");// [] // + this.logger.info(`onPlayerEvent::onPlayerEvent ${JSON.stringify(event)}`); +//%if module + this.sendRcon("/sc ipc___plugin_name__.foo()"); +//%endif } async handlePluginExampleEvent(event: PluginExampleEvent) { @@ -41,9 +47,11 @@ export class InstancePlugin extends BaseInstancePlugin { myResponseString: request.myString, myResponseNumbers: request.myNumberArray, }; - }// [module] // + } +//%if module async handlePluginExampleIPC(event: PuginExampleIPC) { this.logger.info(JSON.stringify(event)); - }// [] // + } +//%endif } diff --git a/packages/create/templates/plugin-ts/messages.ts b/packages/create/templates/plugin-ts/messages.ts index 5d148bdc..f7efef57 100644 --- a/packages/create/templates/plugin-ts/messages.ts +++ b/packages/create/templates/plugin-ts/messages.ts @@ -6,8 +6,8 @@ export class PluginExampleEvent { static type = "event" as const; static src = ["host", "control"] as const; static dst = ["controller", "host", "instance"] as const; - static plugin = "// plugin_name //" as const; - static permission = "// plugin_name //.example.permission.event"; + static plugin = "__plugin_name__" as const; + static permission = "__plugin_name__.example.permission.event"; constructor( public myString: string, @@ -30,8 +30,8 @@ export class PluginExampleRequest { static type = "request" as const; static src = ["host", "control"] as const; static dst = ["controller", "host", "instance"] as const; - static plugin = "// plugin_name //" as const; - static permission = "// plugin_name //.example.permission.request"; + static plugin = "__plugin_name__" as const; + static permission = "__plugin_name__.example.permission.request"; constructor( public myString: string, @@ -52,7 +52,8 @@ export class PluginExampleRequest { "myResponseString": Type.String(), "myResponseNumbers": Type.Array(Type.Number()), })); -}// [subscribable] // +} +//%if controller & web // Subscribing requires web content and the controller export class ExampleSubscribableValue { constructor( @@ -78,8 +79,8 @@ export class ExampleSubscribableUpdate { static type = "event" as const; static src = "controller" as const; static dst = "control" as const; - static plugin = "// plugin_name //" as const; - static permission = "// plugin_name //.example.permission.subscribe"; + static plugin = "__plugin_name__" as const; + static permission = "__plugin_name__.example.permission.subscribe"; constructor( public updates: ExampleSubscribableValue[], @@ -92,4 +93,5 @@ export class ExampleSubscribableUpdate { static fromJSON(json: Static) { return new this(json.updates.map(update => ExampleSubscribableValue.fromJSON(update))); } -}// [] //// [] // +} +//%endif diff --git a/packages/create/templates/plugin-ts/module/module.json b/packages/create/templates/plugin-ts/module/module.json deleted file mode 100644 index 33915ac9..00000000 --- a/packages/create/templates/plugin-ts/module/module.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "// plugin_name //", - "load": [ - "control.lua" - ], - "require": [ - // [instance] //"module_exports.lua"// [] // - ], - "dependencies": [ - - ] -} diff --git a/packages/create/templates/plugin-ts/package.json b/packages/create/templates/plugin-ts/package.json deleted file mode 100644 index f1a25265..00000000 --- a/packages/create/templates/plugin-ts/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "// plugin_name //", - "version": "0.1.0", - "description": "", - "main": "dist/plugin/index.js", - "scripts": { - "prepare": "// prepare //" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@clusterio/lib": "^2.0.0-alpha.14" - }, - "devDependencies": { - // dev_dependencies // - "@clusterio/lib": "^2.0.0-alpha.14" - }, - "dependencies": { - "@sinclair/typebox": "^0.30.4" - }, - "publishConfig": { - "access": "public" - }, - "keywords": [ - "clusterio", - "factorio" - ] -} diff --git a/packages/create/templates/plugin-ts/tsconfig.browser.json b/packages/create/templates/plugin-ts/tsconfig.browser.json new file mode 100644 index 00000000..e1b39d0f --- /dev/null +++ b/packages/create/templates/plugin-ts/tsconfig.browser.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.browser.json", + "references": [ + { "path": "../../packages/lib/tsconfig.browser.json" }, + { "path": "../../packages/web_ui/tsconfig.browser.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/browser", + } +} diff --git a/packages/create/templates/plugin-ts/tsconfig.json b/packages/create/templates/plugin-ts/tsconfig.json index 087576d6..136e8ab6 100644 --- a/packages/create/templates/plugin-ts/tsconfig.json +++ b/packages/create/templates/plugin-ts/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../../tsconfig.node.json", + "files": [], "references": [ - { "path": "../../packages/lib" }, - ], - "include": ["./**/*.ts"], - "exclude": ["test/*", "./dist/*"], - "compilerOptions": { - "outDir": "dist/plugin", - }, +//%if web + { "path": "./tsconfig.browser.json" }, +//%endif + { "path": "./tsconfig.node.json" } + ] } diff --git a/packages/create/templates/plugin-ts/tsconfig.node.json b/packages/create/templates/plugin-ts/tsconfig.node.json new file mode 100644 index 00000000..b6197500 --- /dev/null +++ b/packages/create/templates/plugin-ts/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.node.json", + "references": [ + { "path": "../../packages/lib/tsconfig.node.json" }, + ], + "include": ["./**/*.ts"], + "exclude": ["test/*", "./dist/*"], + "compilerOptions": { + "outDir": "dist/node", + }, +} diff --git a/packages/create/templates/plugin-ts/tsconfig.web.json b/packages/create/templates/plugin-ts/tsconfig.web.json deleted file mode 100644 index c37f9141..00000000 --- a/packages/create/templates/plugin-ts/tsconfig.web.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.web.json", - "references": [ - { "path": "../../packages/lib/tsconfig.web.json" }, - { "path": "../../packages/web_ui/tsconfig.web.json" }, - ], - "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], - "compilerOptions": { - "outDir": "dist/web/build", - } -} diff --git a/packages/create/templates/plugin-ts/web/plugin.tsx b/packages/create/templates/plugin-ts/web/plugin.tsx index f1a8c229..ff0b6393 100644 --- a/packages/create/templates/plugin-ts/web/plugin.tsx +++ b/packages/create/templates/plugin-ts/web/plugin.tsx @@ -1,46 +1,68 @@ -import React, { useContext, useEffect, useState/*/ [subscribable] /*/, useCallback, useSyncExternalStore/*/ [] /*/ } from "react"; -// import { } from "antd"; +import React, { + useContext, useEffect, useState, +//%if controller & web // Subscribing requires web content and the controller + useCallback, useSyncExternalStore, +//%endif +} from "react"; + +// import { +// +// } from "antd"; import { BaseWebPlugin, PageLayout, Control, ControlContext, notifyErrorHandler, } from "@clusterio/web_ui"; +import { + PluginExampleEvent, PluginExampleRequest, +//%if controller & web // Subscribing requires web content and the controller + ExampleSubscribableUpdate, +//%endif +} from "../messages"; + import * as lib from "@clusterio/lib"; -import { PluginExampleEvent, PluginExampleRequest/*/ [subscribable] /*/, ExampleSubscribableUpdate/*/ [] /*/ } from "../messages"; function MyTemplatePage() { - const control = useContext(ControlContext);// [subscribable] // - const plugin = control.plugins.get("// plugin_name //") as WebPlugin; - const [subscribableData, synced] = plugin.useSubscribableData();// [] // + const control = useContext(ControlContext); +//%if controller & web // Subscribing requires web content and the controller + const plugin = control.plugins.get("__plugin_name__") as WebPlugin; + const [subscribableData, synced] = plugin.useSubscribableData(); +//%endif - return -

// plugin_name //

// [subscribable] // - Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])}// [] // + return +

__plugin_name__

+//%if controller & web // Subscribing requires web content and the controller + Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])} +//%endif
; } -export class WebPlugin extends BaseWebPlugin {// [subscribable] // +export class WebPlugin extends BaseWebPlugin { +//%if controller & web // Subscribing requires web content and the controller subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control); - // [] // + +//%endif async init() { this.pages = [ { - path: "/// plugin_name //", - sidebarName: "// plugin_name //", - permission: "// plugin_name //.page.view", + path: "/__plugin_name__", + sidebarName: "__plugin_name__", + permission: "__plugin_name__.page.view", content: , }, ]; this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); - }// [subscribable] // + } +//%if controller & web // Subscribing requires web content and the controller useSubscribableData() { const control = useContext(ControlContext); const subscribe = useCallback((callback: () => void) => this.subscribableData.subscribe(callback), [control]); return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot()); - }// [] // + } +//%endif async handlePluginExampleEvent(event: PluginExampleEvent) { this.logger.info(JSON.stringify(event)); diff --git a/packages/create/templates/plugin-ts/webpack.config.js b/packages/create/templates/plugin-ts/webpack.config.js deleted file mode 100644 index 81e247eb..00000000 --- a/packages/create/templates/plugin-ts/webpack.config.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const path = require("path"); -const webpack = require("webpack"); -const { merge } = require("webpack-merge"); - -const common = require("@clusterio/web_ui/webpack.common"); - -module.exports = (env = {}) => merge(common(env), { - context: __dirname, - entry: "./web/index.tsx", - output: { - path: path.resolve(__dirname, "dist", "web"), - }, - plugins: [ - new webpack.container.ModuleFederationPlugin({ - name: "// plugin_name //", - library: { type: "var", name: "plugin_// plugin_name //" }, - exposes: { - "./": "./index.ts", - "./package.json": "./package.json", - }, - shared: { - "@clusterio/lib": { import: false }, - "@clusterio/web_ui": { import: false }, - }, - }), - ], -}); diff --git a/packages/create/templates/readmd.md b/packages/create/templates/readmd.md new file mode 100644 index 00000000..7a47444c --- /dev/null +++ b/packages/create/templates/readmd.md @@ -0,0 +1,114 @@ +# Templates + +Create has the ability to create new plugins from templates to avoid needing to manually copy lots of boiler plate. This folder contains all those templates for both Javascript and Typescript plugins. These templates have custom preprocessor instructions that can be used to alter a template based on developer input, this syntax is described below. + +- [The Basics](#the-basics) +- [Insert Instruction](#insert-instruction) (`__variable__`) +- [Conditional Instruction](#conditional-instruction) (`//%if` `//%endif`) +- [Available Variables](#available-variables) + +## The Basics + +All files can contain preprocessor instructions whether json, js, lua, or anything else. All instructions use the same syntax although it has been designed to work well with js and ts files. + +Except for the insert instruction, all preprocessors use a `//%` prefix and must be on their own line. These lines are never included in the output. Parsing is stopped when `//` is encountered to allow for comments on preprocessors. Eg. `//%if foo // this is a comment`. + +Empty instructions, aka just comments, can be inserted into templates using `//%//` and are not included in the output file. + +`templates/plugin-js` contains all the files which will be used by a javascript only plugin. +`templates/plugin-ts` contains all the files which will be used by a plugin that supports typescript. +`templates/common` contains files used by both types of plugins, this is mostly lua code or root files like `package.json` + +Files are not copied via wildcards and instead are explicitly listed within `template.js` so any new templates will need to be added there. + +## Insert Instruction + +There are a number of template variables available to be inserted into templates. All variables are raw string values, that is they are inserted directly into the output file without any modifications. To signify a place to insert a variable you need only surround its identifier with double underscores, examples below. + +Example Variables: + +```sh +my_number=1 +my_string=foo +my_property=bar: "baz" +``` + +Example Template: + +```ts +const __my_string__: number = __my_number__; +console.log(__my_string__); + +console.log({ + foo: "Hello, __my_string__!", + __my_property__, + my_property: __my_string__, +}); +``` + +Example Output: + +```ts +const foo: number = 1; +console.log(foo); + +console.log({ + foo: "Hello, foo!", + bar: "baz", + my_property: foo, +}); +``` + +## Conditional Instruction + +Conditionals allow for code to be included/omitted based on the value of template variables. Currently only boolean logic in [disjunctive normal form](https://en.wikipedia.org/wiki/Disjunctive_normal_form) is supported and any variables that are not booleans are evaluated with the [Boolean constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean). + +Variables used in condition statements do not require double underscores. Their values can be inverted using `!`. Arithmetic and comparison operations are not supported. Parentheses are also not supported, conditions must be in DNF. + +Conversion to DNF: +`A & (B | !C)` -> `A & B | A & !C` evaluated as `(A and B) or (A and not C)` + +Example Variables: + +```sh +my_string=foo +say_hello=true +say_done=true +verbose=false +``` + +Example Template: + +```ts +//%if verbose +console.log("Starting..."); +//%endif +//%if say_hello & my_string +console.log("Hello, __my_string__!"); +//%endif +//%if say_done | verbose +console.log("Done."); +//%endif +``` + +Example Output: + +```ts +console.log("Hello, foo!"); +console.log("Done."); +``` + +## Available Variables + +| Identifier | Example | Description | +| - | - | - | +| typescript | `true` / `false` | `true` if typescript templates are included | +| controller | `true` / `false` | `true` if controller templates are included | +| host | `true` / `false` | `true` if host templates are included | +| instance | `true` / `false` | `true` if instance templates are included | +| module | `true` / `false` | `true` if module templates are included | +| ctl | `true` / `false` | `true` if control templates are included | +| web | `true` / `false` | `true` if web templates are included | +| plugin_name | `my_plugin` | name of the plugin | +| prepare | `tsc --build` | commands to run for the prepare script | +| ext | `ts` / `js` | file extension of source files |