mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
Setup exp_scenario for commands
This commit is contained in:
3
exp_scenario/.gitignore
vendored
Normal file
3
exp_scenario/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
dist/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
1697
exp_scenario/.luacheckrc
Normal file
1697
exp_scenario/.luacheckrc
Normal file
File diff suppressed because it is too large
Load Diff
3
exp_scenario/.npmignore
Normal file
3
exp_scenario/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
7
exp_scenario/controller.ts
Normal file
7
exp_scenario/controller.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
import { BaseControllerPlugin, InstanceInfo } from "@clusterio/controller";
|
||||
|
||||
export class ControllerPlugin extends BaseControllerPlugin {
|
||||
async init() {
|
||||
}
|
||||
}
|
||||
37
exp_scenario/index.ts
Normal file
37
exp_scenario/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
import * as Messages from "./messages";
|
||||
|
||||
lib.definePermission({
|
||||
name: "exp_scenario.config.view",
|
||||
title: "View ExpScenario Config",
|
||||
description: "View the config for all submodules of ExpScenario",
|
||||
});
|
||||
|
||||
lib.definePermission({
|
||||
name: "exp_scenario.config.edit",
|
||||
title: "Edit ExpScenario Config",
|
||||
description: "Edit the config for all submodules of ExpScenario",
|
||||
});
|
||||
|
||||
declare module "@clusterio/lib" {
|
||||
|
||||
}
|
||||
|
||||
export const plugin: lib.PluginDeclaration = {
|
||||
name: "exp_scenario",
|
||||
title: "exp_scenario",
|
||||
description: "Example Description. Plugin. Change me in index.ts",
|
||||
controllerEntrypoint: "./dist/node/controller",
|
||||
instanceEntrypoint: "./dist/node/instance",
|
||||
|
||||
messages: [
|
||||
Messages.PluginExampleEvent,
|
||||
Messages.PluginExampleRequest,
|
||||
Messages.ExampleSubscribableUpdate,
|
||||
],
|
||||
|
||||
webEntrypoint: "./web",
|
||||
routes: [
|
||||
"/exp_scenario",
|
||||
],
|
||||
};
|
||||
10
exp_scenario/instance.ts
Normal file
10
exp_scenario/instance.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
import { BaseInstancePlugin } from "@clusterio/host";
|
||||
|
||||
export class InstancePlugin extends BaseInstancePlugin {
|
||||
async init() {
|
||||
}
|
||||
|
||||
async onStart() {
|
||||
}
|
||||
}
|
||||
95
exp_scenario/messages.ts
Normal file
95
exp_scenario/messages.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { plainJson, jsonArray, JsonBoolean, JsonNumber, JsonString, StringEnum } from "@clusterio/lib";
|
||||
import { Type, Static } from "@sinclair/typebox";
|
||||
|
||||
export class PluginExampleEvent {
|
||||
declare ["constructor"]: typeof PluginExampleEvent;
|
||||
static type = "event" as const;
|
||||
static src = ["host", "control"] as const;
|
||||
static dst = ["controller", "host", "instance"] as const;
|
||||
static plugin = "exp_scenario" as const;
|
||||
static permission = "exp_scenario.example.permission.event";
|
||||
|
||||
constructor(
|
||||
public myString: string,
|
||||
public myNumberArray: number[],
|
||||
) {
|
||||
}
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
"myString": Type.String(),
|
||||
"myNumberArray": Type.Array(Type.Number()),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof PluginExampleEvent.jsonSchema>) {
|
||||
return new PluginExampleEvent(json.myString, json.myNumberArray);
|
||||
}
|
||||
}
|
||||
|
||||
export class PluginExampleRequest {
|
||||
declare ["constructor"]: typeof PluginExampleRequest;
|
||||
static type = "request" as const;
|
||||
static src = ["host", "control"] as const;
|
||||
static dst = ["controller", "host", "instance"] as const;
|
||||
static plugin = "exp_scenario" as const;
|
||||
static permission = "exp_scenario.example.permission.request";
|
||||
|
||||
constructor(
|
||||
public myString: string,
|
||||
public myNumberArray: number[],
|
||||
) {
|
||||
}
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
"myString": Type.String(),
|
||||
"myNumberArray": Type.Array(Type.Number()),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof PluginExampleRequest.jsonSchema>) {
|
||||
return new PluginExampleRequest(json.myString, json.myNumberArray);
|
||||
}
|
||||
|
||||
static Response = plainJson(Type.Object({
|
||||
"myResponseString": Type.String(),
|
||||
"myResponseNumbers": Type.Array(Type.Number()),
|
||||
}));
|
||||
}
|
||||
|
||||
export class ExampleSubscribableValue {
|
||||
constructor(
|
||||
public id: string,
|
||||
public updatedAtMs: number,
|
||||
public isDeleted: boolean,
|
||||
) {
|
||||
}
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
id: Type.String(),
|
||||
updatedAtMs: Type.Number(),
|
||||
isDeleted: Type.Boolean(),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
||||
return new this(json.id, json.updatedAtMs, json.isDeleted);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExampleSubscribableUpdate {
|
||||
declare ["constructor"]: typeof ExampleSubscribableUpdate;
|
||||
static type = "event" as const;
|
||||
static src = "controller" as const;
|
||||
static dst = "control" as const;
|
||||
static plugin = "exp_scenario" as const;
|
||||
static permission = "exp_scenario.example.permission.subscribe";
|
||||
|
||||
constructor(
|
||||
public updates: ExampleSubscribableValue[],
|
||||
) { }
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
"updates": Type.Array(ExampleSubscribableValue.jsonSchema),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
||||
return new this(json.updates.map(update => ExampleSubscribableValue.fromJSON(update)));
|
||||
}
|
||||
}
|
||||
19
exp_scenario/module/commands/authorities.lua
Normal file
19
exp_scenario/module/commands/authorities.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
local Commands = require("modules/exp_commands")
|
||||
local add, allow, deny = Commands.add_permission_authority, Commands.status.success, Commands.status.unauthorised
|
||||
|
||||
local Roles = require("modules/exp_legacy/expcore/roles")
|
||||
|
||||
local authorities = {}
|
||||
|
||||
--- If a command has the flag "character_only" then the command can only be used outside of remote view
|
||||
authorities.exp_permission =
|
||||
add(function(player, command)
|
||||
if not Roles.player_allowed(player, command.flags.exp_permission or ("command/" .. command)) then
|
||||
return deny{ "exp-commands-authorities_role.deny" }
|
||||
else
|
||||
return allow()
|
||||
end
|
||||
end)
|
||||
|
||||
return authorities
|
||||
97
exp_scenario/module/commands/types.lua
Normal file
97
exp_scenario/module/commands/types.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
--[[-- Command Types - Roles
|
||||
The data types that are used with exp_roles
|
||||
|
||||
Adds parsers for:
|
||||
role
|
||||
lower_role
|
||||
lower_role_player
|
||||
lower_role_player_online
|
||||
lower_role_player_alive
|
||||
]]
|
||||
|
||||
local Commands = require("modules/exp_commands")
|
||||
local add, parse = Commands.add_data_type, Commands.parse_input
|
||||
local valid, invalid = Commands.status.success, Commands.status.invalid_input
|
||||
|
||||
local Roles = require("modules.exp_legacy.expcore.roles")
|
||||
local highest_role = Roles.get_player_highest_role
|
||||
local key_of_roles = Commands.types.key_of(Roles.config.roles)
|
||||
|
||||
local types = {}
|
||||
|
||||
--- A role defined by exp roles
|
||||
--- @type Commands.InputParser
|
||||
types.role =
|
||||
add("role", function(input, player)
|
||||
local _, status, rtn = parse(input, player, key_of_roles)
|
||||
return status, rtn
|
||||
end)
|
||||
|
||||
--- A role which is lower than the players highest role
|
||||
--- @type Commands.InputParser
|
||||
types.lower_role =
|
||||
add("lower_role", function(input, player)
|
||||
local success, status, result = parse(input, player, types.role)
|
||||
if not success then return status, result end
|
||||
--- @cast result any TODO role is not a defined type
|
||||
|
||||
local player_highest = highest_role(player)
|
||||
if player_highest.index >= result.index then
|
||||
return invalid{ "exp-commands-parse_role.lower-role" }
|
||||
else
|
||||
return valid(result)
|
||||
end
|
||||
end)
|
||||
|
||||
--- A player who is of a lower role than the executing player
|
||||
--- @type Commands.InputParser
|
||||
types.lower_role_player =
|
||||
add("lower_role_player", function(input, player)
|
||||
local success, status, result = parse(input, player, Commands.types.player)
|
||||
if not success then return status, result end
|
||||
--- @cast result LuaPlayer
|
||||
|
||||
local other_highest = highest_role(result)
|
||||
local player_highest = highest_role(player)
|
||||
if player_highest.index < other_highest.index then
|
||||
return invalid{ "exp-commands-parse_role.lower-role-player" }
|
||||
else
|
||||
return valid(result)
|
||||
end
|
||||
end)
|
||||
|
||||
--- A player who is of a lower role than the executing player
|
||||
--- @type Commands.InputParser
|
||||
types.lower_role_player_online =
|
||||
add("lower_role_player", function(input, player)
|
||||
local success, status, result = parse(input, player, Commands.types.player_online)
|
||||
if not success then return status, result end
|
||||
--- @cast result LuaPlayer
|
||||
|
||||
local other_highest = highest_role(result)
|
||||
local player_highest = highest_role(player)
|
||||
if player_highest.index < other_highest.index then
|
||||
return invalid{ "exp-commands-parse_role.lower-role-player" }
|
||||
else
|
||||
return valid(result)
|
||||
end
|
||||
end)
|
||||
|
||||
--- A player who is of a lower role than the executing player
|
||||
types.lower_role_player_alive =
|
||||
add("lower_role_player", function(input, player)
|
||||
local success, status, result = parse(input, player, Commands.types.player_alive)
|
||||
if not success then return status, result end
|
||||
--- @cast result LuaPlayer
|
||||
|
||||
local other_highest = highest_role(result)
|
||||
local player_highest = highest_role(player)
|
||||
if player_highest.index < other_highest.index then
|
||||
return invalid{ "exp-commands-parse_role.lower-role-player" }
|
||||
else
|
||||
return valid(result)
|
||||
end
|
||||
end)
|
||||
|
||||
return types
|
||||
9
exp_scenario/module/control.lua
Normal file
9
exp_scenario/module/control.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
--- Command Extensions
|
||||
require("modules.exp_scenario/commands/types")
|
||||
require("modules.exp_scenario/commands/authorities")
|
||||
|
||||
--- Commands
|
||||
require("modules/exp_scenario/commands/admin_chat")
|
||||
require("modules/exp_scenario/commands/bot_queues")
|
||||
require("modules/exp_scenario/commands/cheat")
|
||||
6
exp_scenario/module/locale/en.cfg
Normal file
6
exp_scenario/module/locale/en.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[exp-commands-authorities_role]
|
||||
deny=Unauthorized, Access is denied due to missing permissions
|
||||
|
||||
[exp-commands-parse_role]
|
||||
lower-role=Role is higher than your highest.
|
||||
lower-role-player=Player has a higher role.
|
||||
13
exp_scenario/module/module.json
Normal file
13
exp_scenario/module/module.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "exp_scenario",
|
||||
"load": [
|
||||
],
|
||||
"require": [
|
||||
"control.lua"
|
||||
],
|
||||
"dependencies": {
|
||||
"clusterio": "*",
|
||||
"exp_util": "*",
|
||||
"exp_commands": "*"
|
||||
}
|
||||
}
|
||||
38
exp_scenario/package.json
Normal file
38
exp_scenario/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "exp_scenario",
|
||||
"version": "0.1.0",
|
||||
"description": "Example Description. Package. Change me in package.json",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@clusterio/lib": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.3",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"antd": "^5.13.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-merge": "^5.9.0",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "^0.30.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"clusterio",
|
||||
"factorio"
|
||||
]
|
||||
}
|
||||
4
exp_scenario/tsconfig.browser.json
Normal file
4
exp_scenario/tsconfig.browser.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../tsconfig.browser.json",
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
}
|
||||
7
exp_scenario/tsconfig.json
Normal file
7
exp_scenario/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
5
exp_scenario/tsconfig.node.json
Normal file
5
exp_scenario/tsconfig.node.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig.node.json",
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
}
|
||||
68
exp_scenario/web/index.tsx
Normal file
68
exp_scenario/web/index.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React, {
|
||||
useContext, useEffect, useState,
|
||||
useCallback, useSyncExternalStore,
|
||||
} from "react";
|
||||
|
||||
// import {
|
||||
//
|
||||
// } from "antd";
|
||||
|
||||
import {
|
||||
BaseWebPlugin, PageLayout, PageHeader, Control, ControlContext, notifyErrorHandler,
|
||||
} from "@clusterio/web_ui";
|
||||
|
||||
import {
|
||||
PluginExampleEvent, PluginExampleRequest,
|
||||
ExampleSubscribableUpdate, ExampleSubscribableValue,
|
||||
} from "../messages";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
function MyTemplatePage() {
|
||||
const control = useContext(ControlContext);
|
||||
const plugin = control.plugins.get("exp_scenario") as WebPlugin;
|
||||
const [subscribableData, synced] = plugin.useSubscribableData();
|
||||
|
||||
return <PageLayout nav={[{ name: "exp_scenario" }]}>
|
||||
<PageHeader title="exp_scenario" />
|
||||
Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])}
|
||||
</PageLayout>;
|
||||
}
|
||||
|
||||
export class WebPlugin extends BaseWebPlugin {
|
||||
subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control);
|
||||
|
||||
async init() {
|
||||
this.pages = [
|
||||
{
|
||||
path: "/exp_scenario",
|
||||
sidebarName: "exp_scenario",
|
||||
// This permission is client side only, so it must match the permission string of a resource request to be secure
|
||||
// An undefined value means that the page will always be visible
|
||||
permission: "exp_scenario.example.permission.subscribe",
|
||||
content: <MyTemplatePage/>,
|
||||
},
|
||||
];
|
||||
|
||||
this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this));
|
||||
this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));
|
||||
}
|
||||
|
||||
useSubscribableData() {
|
||||
const control = useContext(ControlContext);
|
||||
const subscribe = useCallback((callback: () => void) => this.subscribableData.subscribe(callback), [control]);
|
||||
return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot());
|
||||
}
|
||||
|
||||
async handlePluginExampleEvent(event: PluginExampleEvent) {
|
||||
this.logger.info(JSON.stringify(event));
|
||||
}
|
||||
|
||||
async handlePluginExampleRequest(request: PluginExampleRequest) {
|
||||
this.logger.info(JSON.stringify(request));
|
||||
return {
|
||||
myResponseString: request.myString,
|
||||
myResponseNumbers: request.myNumberArray,
|
||||
};
|
||||
}
|
||||
}
|
||||
32
exp_scenario/webpack.config.js
Normal file
32
exp_scenario/webpack.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
"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: "exp_scenario",
|
||||
library: { type: "window", name: "plugin_exp_scenario" },
|
||||
exposes: {
|
||||
"./": "./index.ts",
|
||||
"./package.json": "./package.json",
|
||||
"./web": "./web/index.tsx",
|
||||
},
|
||||
shared: {
|
||||
"@clusterio/lib": { import: false },
|
||||
"@clusterio/web_ui": { import: false },
|
||||
"antd": { import: false },
|
||||
"react": { import: false },
|
||||
"react-dom": { import: false },
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user