check group and var on build

This commit is contained in:
codetypes committed 2025-03-19 09:38:29 +08:00
1 parent 4fb851c8cc
commit b8817fd2c9
10 files changed
+163 -60

No files matched your search

+12 -2
View File
@@ -5,6 +5,7 @@ import os from "node:os";
import path from "node:path"; import path from "node:path";
import { argv } from "node:process"; import { argv } from "node:process";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { VERSION, type FileVarDecl } from "../../src/misc/b3type";
import * as b3util from "../../src/misc/b3util"; import * as b3util from "../../src/misc/b3util";
import Path from "../../src/misc/path"; import Path from "../../src/misc/path";
@@ -30,7 +31,7 @@ for (let i = 0; i < argv.length; i++) {
} }
const printHelp = () => { const printHelp = () => {
console.log("Usage: Behavior3 Editor [options]"); console.log(`Usage: Behavior3 Editor ${VERSION} [options]`);
console.log("Options:"); console.log("Options:");
console.log(" --project <path> Set the project path"); console.log(" --project <path> Set the project path");
console.log(" --build <path> Set the build output path"); console.log(" --build <path> Set the build output path");
@@ -64,11 +65,20 @@ if (buildOutput || buildProject || buildHelp) {
if (path.endsWith(".json")) { if (path.endsWith(".json")) {
const buildpath = buildDir + "/" + path.substring(workdir.length + 1); const buildpath = buildDir + "/" + path.substring(workdir.length + 1);
const treeModel = b3util.createBuildData(path); const treeModel = b3util.createBuildData(path);
if (treeModel && treeModel.export === false) { if (!treeModel) {
continue;
}
if (treeModel.export === false) {
console.log("skip:", buildpath); console.log("skip:", buildpath);
continue; continue;
} }
console.log("build:", buildpath); console.log("build:", buildpath);
const declare: FileVarDecl = {
import: treeModel.import.map((v) => ({ path: v, vars: [], depends: [] })),
declvar: treeModel.declvar.map((v) => ({ name: v.name, desc: v.desc })),
subtree: [],
};
b3util.refreshDeclare(treeModel, declare);
if (!b3util.checkNodeData(treeModel?.root)) { if (!b3util.checkNodeData(treeModel?.root)) {
hasError = true; hasError = true;
} }
+1
View File
@@ -2,6 +2,7 @@
"about": "About Behavior3", "about": "About Behavior3",
"add": "Add", "add": "Add",
"actuaSize": "Actual Size", "actuaSize": "Actual Size",
"alertNewVersion": "This file is created by a newer version of Behavior3({{version}}), please upgrade to the latest version.",
"build": "Build", "build": "Build",
"batch": "Batch Script", "batch": "Batch Script",
"batchCompleted": "Batch completed", "batchCompleted": "Batch completed",
+1
View File
@@ -2,6 +2,7 @@
"about": "关于 Behavior3", "about": "关于 Behavior3",
"add": "添加", "add": "添加",
"actualSize": "实际大小", "actualSize": "实际大小",
"alertNewVersion": "此文件由新版本 Behavior3({{version}}) 创建,请升级到最新版本。",
"build": "构建", "build": "构建",
"batch": "批处理脚本", "batch": "批处理脚本",
"batchCompleted": "批处理完成", "batchCompleted": "批处理完成",
+6 -19
View File
@@ -3,13 +3,8 @@
"desc": "", "desc": "",
"firstid": 1, "firstid": 1,
"export": true, "export": true,
"group": [ "group": ["Client"],
"Client" "import": ["vars/declare-core.json", "vars/declare-vars.json"],
],
"import": [
"vars/declare-core.json",
"vars/declare-vars.json"
],
"declvar": [ "declvar": [
{ {
"name": "target", "name": "target",
@@ -33,16 +28,12 @@
"id": 4, "id": 4,
"name": "FindEnemy", "name": "FindEnemy",
"args": {}, "args": {},
"output": [ "output": ["target1"]
"target1"
]
}, },
{ {
"id": 5, "id": 5,
"name": "Attack", "name": "Attack",
"input": [ "input": ["target1"]
"target1"
]
}, },
{ {
"id": 6, "id": 6,
@@ -54,16 +45,12 @@
{ {
"id": 7, "id": 7,
"name": "MoveToTarget", "name": "MoveToTarget",
"input": [ "input": ["target"]
"target"
]
}, },
{ {
"id": 8, "id": 8,
"name": "Clear", "name": "Clear",
"output": [ "output": ["test"],
"test"
],
"children": [ "children": [
{ {
"id": 9, "id": 9,
+34 -14
View File
@@ -21,7 +21,15 @@ import {
EditTree, EditTree,
useWorkspace, useWorkspace,
} from "../contexts/workspace-context"; } from "../contexts/workspace-context";
import { ImportDef, isExprType, NodeModel, TreeGraphData, TreeModel, VarDef } from "../misc/b3type"; import {
ImportDef,
isExprType,
NodeModel,
TreeGraphData,
TreeModel,
VarDef,
VERSION,
} from "../misc/b3type";
import * as b3util from "../misc/b3util"; import * as b3util from "../misc/b3util";
import { message } from "../misc/hooks"; import { message } from "../misc/hooks";
import i18n from "../misc/i18n"; import i18n from "../misc/i18n";
@@ -777,20 +785,32 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
}; };
const save = () => { const save = () => {
if (editor.unsave || !fs.existsSync(editor.path)) { if (b3util.isNewVersion(editor.data.version)) {
const path = editor.path; message.error(t("alertNewVersion", { version: editor.data.version }), 6);
editor.autoId = b3util.refreshTreeDataId(editor.root, editor.data.firstid); return;
editor.data.root = b3util.createFileData(editor.root);
editor.modifiedTime = Date.now();
fs.writeFileSync(path, JSON.stringify(editor.data, null, 2));
workspace.updateFileMeta(editor);
editor.unsave = false;
editor.graph.changeData(editor.root);
editor.graph.layout();
restoreViewport();
updateSearchState();
updateState();
} }
const path = editor.path;
editor.autoId = b3util.refreshTreeDataId(editor.root, editor.data.firstid);
editor.data.root = b3util.createFileData(editor.root);
editor.modifiedTime = Date.now();
fs.writeFileSync(
path,
JSON.stringify(
{
...editor.data,
version: VERSION,
},
null,
2
)
);
workspace.updateFileMeta(editor);
editor.unsave = false;
editor.graph.changeData(editor.root);
editor.graph.layout();
restoreViewport();
updateSearchState();
updateState();
}; };
const editSubtree = () => { const editSubtree = () => {
+5 -3
View File
@@ -152,12 +152,14 @@ G6.registerNode(
!checkTreeData(data) || !checkTreeData(data) ||
(nodeDef.group && !usingGroups[nodeDef.group]) || (nodeDef.group && !usingGroups[nodeDef.group]) ||
(usingVars && (usingVars &&
(data.input?.some((v) => usingVars?.[v] === undefined) || (data.input?.some((v) => v && usingVars?.[v] === undefined) ||
data.output?.some((v) => usingVars?.[v] === undefined) || data.output?.some((v) => v && usingVars?.[v] === undefined) ||
nodeDef.args?.some( nodeDef.args?.some(
(v) => (v) =>
isExprType(v.type) && isExprType(v.type) &&
parseExpr(data.args?.[v.name] ?? "").some((vv) => usingVars?.[vv] === undefined) parseExpr(data.args?.[v.name] ?? "").some(
(vv) => vv && usingVars?.[vv] === undefined
)
))) )))
) { ) {
classify = "Error"; classify = "Error";
+24 -19
View File
@@ -5,7 +5,14 @@ import * as fs from "fs";
import React from "react"; import React from "react";
import { create } from "zustand"; import { create } from "zustand";
import { NodeDef } from "../behavior3/src/behavior3"; import { NodeDef } from "../behavior3/src/behavior3";
import { ImportDef, NodeModel, TreeGraphData, TreeModel, VarDef } from "../misc/b3type"; import {
FileVarDecl,
ImportDef,
NodeModel,
TreeGraphData,
TreeModel,
VarDef,
} from "../misc/b3type";
import * as b3util from "../misc/b3util"; import * as b3util from "../misc/b3util";
import { message } from "../misc/hooks"; import { message } from "../misc/hooks";
import i18n from "../misc/i18n"; import i18n from "../misc/i18n";
@@ -46,11 +53,7 @@ export class EditorStore {
data: TreeModel; data: TreeModel;
root: TreeGraphData; root: TreeGraphData;
declare: { declare: FileVarDecl;
import: ImportDef[];
subtree: ImportDef[];
declvar: VarDef[];
};
autoId: number = 1; autoId: number = 1;
dragSrcId?: string; dragSrcId?: string;
@@ -354,11 +357,20 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
workspace.allFiles.forEach((file) => { workspace.allFiles.forEach((file) => {
const buildpath = buildDir + "/" + workspace.relative(file.path); const buildpath = buildDir + "/" + workspace.relative(file.path);
const treeModel = b3util.createBuildData(file.path); const treeModel = b3util.createBuildData(file.path);
if (treeModel && treeModel.export === false) { if (!treeModel) {
return;
}
if (treeModel.export === false) {
console.log("skip:", buildpath); console.log("skip:", buildpath);
return; return;
} }
console.log("build:", buildpath); console.log("build:", buildpath);
const declare: FileVarDecl = {
import: treeModel.import.map((v) => ({ path: v, vars: [], depends: [] })),
declvar: treeModel.declvar.map((v) => ({ name: v.name, desc: v.desc })),
subtree: [],
};
b3util.refreshDeclare(treeModel, declare);
if (!b3util.checkNodeData(treeModel?.root)) { if (!b3util.checkNodeData(treeModel?.root)) {
hasError = true; hasError = true;
} }
@@ -479,6 +491,10 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
set({ editors: workspace.editors }); set({ editors: workspace.editors });
workspace.updateFileMeta(editor); workspace.updateFileMeta(editor);
workspace.edit(editor.path, selectedNode); workspace.edit(editor.path, selectedNode);
if (b3util.isNewVersion(editor.data.version)) {
message.warning(i18n.t("alertNewVersion", { version: editor.data.version }));
}
} catch (error) { } catch (error) {
console.error(error); console.error(error);
message.error(`invalid file: ${path}`); message.error(`invalid file: ${path}`);
@@ -538,18 +554,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
if (!editor) { if (!editor) {
return; return;
} }
const vars: Set<VarDef> = new Set(editor.declare.declvar.slice()); b3util.refreshDeclare(editor.data, editor.declare);
editor.declare.subtree = b3util.findSubtrees(editor.root).map((v) => ({
path: v,
vars: [],
depends: [],
}));
b3util.loadVarDef(editor.declare.import).forEach((v) => vars.add(v));
b3util.loadVarDef(editor.declare.subtree).forEach((v) => {
vars.add(v);
});
b3util.updateUsingGroups(editor.data.group);
b3util.updateUsingVars(Array.from(vars));
}, },
save: () => { save: () => {
+9
View File
@@ -7,6 +7,8 @@ declare module "../behavior3/src/behavior3" {
} }
} }
export const VERSION = "1.8.0";
export type NodeType = NodeDef["type"] | "Other" | "Error"; export type NodeType = NodeDef["type"] | "Other" | "Error";
export type NodeArg = Exclude<NodeDef["args"], undefined>[number]; export type NodeArg = Exclude<NodeDef["args"], undefined>[number];
@@ -51,7 +53,14 @@ export interface ImportDef {
}[]; }[];
} }
export interface FileVarDecl {
import: ImportDef[];
subtree: ImportDef[];
declvar: VarDef[];
}
export interface TreeModel { export interface TreeModel {
version: string;
name: string; name: string;
desc?: string; desc?: string;
export?: boolean; export?: boolean;
+69 -2
View File
@@ -1,6 +1,7 @@
import * as fs from "fs"; import * as fs from "fs";
import { NodeDef } from "../behavior3/src/behavior3"; import { NodeDef } from "../behavior3/src/behavior3";
import { import {
FileVarDecl,
ImportDef, ImportDef,
isBoolType, isBoolType,
isEnumType, isEnumType,
@@ -14,6 +15,7 @@ import {
TreeGraphData, TreeGraphData,
TreeModel, TreeModel,
VarDef, VarDef,
VERSION,
} from "./b3type"; } from "./b3type";
import Path from "./path"; import Path from "./path";
import { readJson, readTree } from "./util"; import { readJson, readTree } from "./util";
@@ -87,6 +89,16 @@ export const parseExpr = (expr: string) => {
return result; return result;
}; };
export const isNewVersion = (version: string) => {
const [major, minor, patch] = version.split(".").map(Number);
const [major2, minor2, patch2] = VERSION.split(".").map(Number);
return (
major > major2 ||
(major === major2 && minor > minor2) ||
(major === major2 && minor === minor2 && patch > patch2)
);
};
export const isValidVariableName = (name: string) => { export const isValidVariableName = (name: string) => {
return /^[a-zA-Z_]/.test(name); return /^[a-zA-Z_]/.test(name);
}; };
@@ -286,6 +298,45 @@ export const checkNodeData = (data: NodeModel | null | undefined) => {
let hasError = false; let hasError = false;
if (conf.group) {
if (!usingGroups[conf.group]) {
error(data, `node group '${conf.group}' is not enabled`);
hasError = true;
}
}
if (usingVars) {
if (data.input) {
for (const v of data.input) {
if (v && !usingVars[v]) {
error(data, `input variable '${v}' is not defined`);
hasError = true;
}
}
}
if (data.output) {
for (const v of data.output) {
if (v && !usingVars[v]) {
error(data, `output variable '${v}' is not defined`);
hasError = true;
}
}
}
if (data.args && conf.args) {
for (const arg of conf.args) {
if (isExprType(arg.type)) {
for (const v of parseExpr(data.args[arg.name] ?? "")) {
if (v && !usingVars[v]) {
error(data, `expr variable '${arg.name}' is not defined`);
hasError = true;
}
}
}
}
}
}
if (conf.children !== undefined && conf.children !== -1) { if (conf.children !== undefined && conf.children !== -1) {
const count = data.children?.length || 0; const count = data.children?.length || 0;
if (conf.children !== count) { if (conf.children !== count) {
@@ -712,6 +763,7 @@ export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) =>
export const createNewTree = (path: string) => { export const createNewTree = (path: string) => {
const tree: TreeModel = { const tree: TreeModel = {
version: VERSION,
name: Path.basenameWithoutExt(path), name: Path.basenameWithoutExt(path),
firstid: 1, firstid: 1,
group: [], group: [],
@@ -796,9 +848,9 @@ export const loadVarDef = (list: ImportDef[]) => {
return Array.from(all); return Array.from(all);
}; };
export const findSubtrees = (data: TreeGraphData) => { const findSubtrees = (data: NodeModel) => {
const list: string[] = []; const list: string[] = [];
const traverse = (v: TreeGraphData) => { const traverse = (v: NodeModel) => {
if (v.path) { if (v.path) {
list.push(v.path); list.push(v.path);
} }
@@ -809,3 +861,18 @@ export const findSubtrees = (data: TreeGraphData) => {
traverse(data); traverse(data);
return list; return list;
}; };
export const refreshDeclare = (tree: TreeModel, declare: FileVarDecl) => {
const vars: Set<VarDef> = new Set(declare.declvar.slice());
declare.subtree = findSubtrees(tree.root).map((v) => ({
path: v,
vars: [],
depends: [],
}));
loadVarDef(declare.import).forEach((v) => vars.add(v));
loadVarDef(declare.subtree).forEach((v) => {
vars.add(v);
});
updateUsingGroups(tree.group);
updateUsingVars(Array.from(vars));
};
+2 -1
View File
@@ -1,5 +1,5 @@
import * as fs from "fs"; import * as fs from "fs";
import type { TreeModel } from "./b3type"; import { VERSION, type TreeModel } from "./b3type";
export const readJson = (path: string) => { export const readJson = (path: string) => {
const str = fs.readFileSync(path, "utf-8"); const str = fs.readFileSync(path, "utf-8");
@@ -9,6 +9,7 @@ export const readJson = (path: string) => {
export const readTree = (path: string) => { export const readTree = (path: string) => {
const str = fs.readFileSync(path, "utf-8"); const str = fs.readFileSync(path, "utf-8");
const data = JSON.parse(str) as TreeModel; const data = JSON.parse(str) as TreeModel;
data.version = data.version ?? VERSION;
data.firstid = data.firstid ?? 1; data.firstid = data.firstid ?? 1;
data.group = data.group || []; data.group = data.group || [];
data.import = data.import || []; data.import = data.import || [];