feat(types): add DataBus system with auto-generated type declarations

This commit is contained in:
2026-01-21 23:53:41 +08:00
parent 9d65768cd3
commit 6178a82d24
8 changed files with 718 additions and 15 deletions

View File

@@ -4,11 +4,41 @@
"workspaces": {
"": {
"devDependencies": {
"@babel/generator": "^7.28.6",
"@babel/parser": "^7.28.6",
"@babel/traverse": "^7.28.6",
"@babel/types": "^7.28.6",
"oxlint": "^1.39.0",
},
},
},
"packages": {
"@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
"@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="],
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
"@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
"@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="],
"@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.39.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lT3hNhIa02xCujI6YGgjmYGg3Ht/X9ag5ipUVETaMpx5Rd4BbTNWUPif1WN1YZHxt3KLCIqaAe7zVhatv83HOQ=="],
"@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.39.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-UT+rfTWd+Yr7iJeSLd/7nF8X4gTYssKh+n77hxl6Oilp3NnG1CKRHxZDy3o3lIBnwgzJkdyUAiYWO1bTMXQ1lA=="],
@@ -25,6 +55,16 @@
"@oxlint/win32-x64": ["@oxlint/win32-x64@1.39.0", "", { "os": "win32", "cpu": "x64" }, "sha512-sbi25lfj74hH+6qQtb7s1wEvd1j8OQbTaH8v3xTcDjrwm579Cyh0HBv1YSZ2+gsnVwfVDiCTL1D0JsNqYXszVA=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"oxlint": ["oxlint@1.39.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.39.0", "@oxlint/darwin-x64": "1.39.0", "@oxlint/linux-arm64-gnu": "1.39.0", "@oxlint/linux-arm64-musl": "1.39.0", "@oxlint/linux-x64-gnu": "1.39.0", "@oxlint/linux-x64-musl": "1.39.0", "@oxlint/win32-arm64": "1.39.0", "@oxlint/win32-x64": "1.39.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.10.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-wSiLr0wjG+KTU6c1LpVoQk7JZ7l8HCKlAkVDVTJKWmCGazsNxexxnOXl7dsar92mQcRnzko5g077ggP3RINSjA=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
}
}

View File

@@ -1,5 +1,6 @@
{
"$schema": "https://www.schemastore.org/jsconfig.json",
"include": ["src/**/*", "types/**/*"],
"compilerOptions": {
"module": "commonjs",
"skipDefaultLibCheck": true,

View File

@@ -1,6 +1,12 @@
{
"dependencies": {},
"scripts": {
"generate-types": "node scripts/AutoExport.js"
},
"devDependencies": {
"@babel/generator": "^7.28.6",
"@babel/parser": "^7.28.6",
"@babel/traverse": "^7.28.6",
"@babel/types": "^7.28.6",
"oxlint": "^1.39.0"
}
}
}

453
scripts/AutoExport.js Normal file
View File

@@ -0,0 +1,453 @@
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const { default: traverse } = require("@babel/traverse");
const parser = require("@babel/parser");
const t = require("@babel/types");
/**
* Recursively find all .js files in a directory
* @param {string} dir - Directory path
* @param {string[]} files - Array to store found files
* @returns {string[]}
*/
function findJsFiles(dir, files = []) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
findJsFiles(fullPath, files);
} else if (entry.isFile() && entry.name.endsWith(".js")) {
files.push(fullPath);
}
}
return files;
}
/**
* Extract JSDoc comment from a node
* @param {import('@babel/types').Node} node
* @returns {string | null}
*/
function extractJSDoc(node) {
if (node.leadingComments && node.leadingComments.length > 0) {
const comment = node.leadingComments[node.leadingComments.length - 1];
if (comment.type === "CommentBlock" && comment.value.startsWith("*")) {
return comment.value;
}
}
return null;
}
/**
* Parse @type annotation from JSDoc
* @param {string | null} jsdoc
* @returns {string | null}
*/
function parseTypeFromJSDoc(jsdoc) {
if (!jsdoc) {
return null;
}
const typeIndex = jsdoc.indexOf("@type");
if (typeIndex === -1) return null;
const openBraceIndex = jsdoc.indexOf("{", typeIndex);
if (openBraceIndex === -1) return null;
// Use stack to find matching closing brace
let stack = 0;
let closeBraceIndex = -1;
for (let i = openBraceIndex; i < jsdoc.length; i++) {
const char = jsdoc[i];
if (char === "{") {
stack++;
} else if (char === "}") {
stack--;
if (stack === 0) {
closeBraceIndex = i;
break;
}
}
}
if (closeBraceIndex === -1) return null;
// Extract type between braces
const typeContent = jsdoc.substring(openBraceIndex + 1, closeBraceIndex);
// Remove JSDoc comment markers and extra whitespace
return formatJSDocType(typeContent);
}
/**
* Parse function type from JSDoc
* @param {string | null} jsdoc
* @returns {string | null}
*/
function parseFunctionTypeFromJSDoc(jsdoc) {
if (!jsdoc) {
return null;
}
// Extract @param tags
const paramMatches = [...jsdoc.matchAll(/@param\s+\{([^}]+)\}\s+(\w+)/g)];
// Extract @returns tag
const returnMatch = jsdoc.match(/@returns\s+\{([^}]+)\}/);
if (paramMatches.length === 0 && !returnMatch) {
return null;
}
// Format parameters and return types
const params = paramMatches.map((m) => {
const paramName = m[2];
const paramType = formatJSDocType(m[1]);
return `${paramName}: ${paramType}`;
}).join(", ");
const returnType = returnMatch ? formatJSDocType(returnMatch[1]) : "void";
return `(${params}) => ${returnType}`;
}
/**
* Infer type from value expression
* @param {import('@babel/types').Node} node
* @returns {string}
*/
function inferTypeFromExpression(node) {
if (t.isNumericLiteral(node)) return "number";
if (t.isStringLiteral(node)) return "string";
if (t.isBooleanLiteral(node)) return "boolean";
// Handle arithmetic expressions (result is number)
if (t.isBinaryExpression(node)) {
return "number";
}
if (t.isArrayExpression(node)) return "any[]";
if (t.isObjectExpression(node)) return "{ [key: string]: any }";
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) return "any => any";
if (t.isIdentifier(node)) return "any";
return "any";
}
/**
* Format multi-line JSDoc type to single line
* @param {string} type
* @returns {string}
*/
function formatJSDocType(type) {
if (!type) return type;
// Replace newlines and extra whitespace
let formatted = type
.split("\n")
.map(line => line.trim())
.filter(line => line.length > 0)
.join(" ");
// Remove JSDoc comment markers (*)
formatted = formatted.replace(/\s*\*\s*/g, " ");
// Clean up extra spaces
formatted = formatted.replace(/\s+/g, " ");
return formatted;
}
/**
* Extract JSDoc from statement before export call
* @param {import('@babel/traverse').NodePath} path
* @returns {string | null}
*/
function findJSDocForExport(path) {
// Get the call expression's statement parent
const statementPath = path.getStatementParent();
if (!statementPath) return null;
const { node } = statementPath;
// Check if there's a standalone JSDoc comment before statement
if (node.leadingComments && node.leadingComments.length > 0) {
for (let i = node.leadingComments.length - 1; i >= 0; i--) {
const comment = node.leadingComments[i];
if (comment.type === "CommentBlock" && comment.value.includes("@type")) {
return comment.value;
}
}
}
return null;
}
/**
* Collect all exports from a file
* @param {string} filePath
* @returns {{ name: string, type: string, sourceFile: string }[]}
*/
function collectExportsFromFile(filePath) {
const content = fs.readFileSync(filePath, "utf-8");
const exports = [];
try {
const ast = parser.parse(content, {
sourceType: "module",
plugins: [],
});
// First pass: collect all variable declarations with their types
const variableTypes = new Map();
traverse(ast, {
VariableDeclarator(path) {
const { node } = path;
if (t.isIdentifier(node.id)) {
const varName = node.id.name;
// Try to get JSDoc from VariableDeclaration parent
let jsdoc = extractJSDoc(path.parent);
// If no JSDoc on parent, try on declarator
if (!jsdoc) {
jsdoc = extractJSDoc(node);
}
let type = parseTypeFromJSDoc(jsdoc) ?? parseFunctionTypeFromJSDoc(jsdoc) ?? inferTypeFromExpression(node.init);
if (type !== "any") {
variableTypes.set(varName, type);
}
}
},
FunctionDeclaration(path) {
const { node } = path;
if (t.isIdentifier(node.id)) {
const funcName = node.id.name;
const jsdoc = extractJSDoc(node);
const type = parseFunctionTypeFromJSDoc(jsdoc);
if (type && type !== "any") {
variableTypes.set(funcName, type);
}
}
},
});
// Second pass: find exports
traverse(ast, {
/**
* Detect: global["name"] = value
*/
AssignmentExpression(babelPath) {
const { node } = babelPath;
const left = node.left;
if (
t.isMemberExpression(left) &&
t.isIdentifier(left.object, { name: "global" }) &&
t.isStringLiteral(left.property)
) {
const exportName = left.property.value;
const jsdoc = extractJSDoc(node);
let type = parseTypeFromJSDoc(jsdoc);
// Try to get type from value identifier
if (!type && t.isIdentifier(node.right)) {
type = variableTypes.get(node.right.name);
}
if (!type) {
type = inferTypeFromExpression(node.right);
}
// Format type to single line
type = formatJSDocType(type);
exports.push({
name: exportName,
type,
sourceFile: path.relative("src", filePath),
});
}
},
/**
* Detect: dataBus.export("name", value)
*/
CallExpression(babelPath) {
const { node } = babelPath;
if (
t.isMemberExpression(node.callee) &&
t.isIdentifier(node.callee.object, { name: "dataBus" }) &&
t.isIdentifier(node.callee.property, { name: "export" }) &&
node.arguments.length >= 2 &&
t.isStringLiteral(node.arguments[0])
) {
const exportName = node.arguments[0].value;
const valueArg = node.arguments[1];
// Check for JSDoc on export statement
let type = parseTypeFromJSDoc(findJSDocForExport(babelPath));
// Try to get type from value identifier
if (!type && t.isIdentifier(valueArg)) {
type = variableTypes.get(valueArg.name);
}
if (!type) {
type = inferTypeFromExpression(valueArg);
}
// Format type to single line
type = formatJSDocType(type);
exports.push({
name: exportName,
type,
sourceFile: path.relative("src", filePath),
});
}
},
});
} catch (error) {
console.error(`Error parsing ${filePath}:`, error.message);
}
return exports;
}
/**
* Generate TypeScript declaration file
* @param {{ name: string, type: string, sourceFile: string }[]} allExports
* @param {string} outputPath
*/
function generateDTS(allExports, outputPath) {
const uniqueExports = new Map();
for (const exp of allExports) {
if (!uniqueExports.has(exp.name)) {
uniqueExports.set(exp.name, exp);
}
}
const sortedExports = Array.from(uniqueExports.values()).sort((a, b) => a.name.localeCompare(b.name));
let dtsContent = `/**
* Auto-generated DataBus type declarations
* Generated by scripts/AutoExport.js
* Do not edit manually
*/
declare global {
/**
* All exported data types
*/
interface ExportTypes {
`;
for (const exp of sortedExports) {
dtsContent += ` /**\n`;
dtsContent += ` * Source: ${exp.sourceFile}\n`;
dtsContent += ` * Type: ${exp.type}\n`;
dtsContent += ` */\n`;
dtsContent += ` "${exp.name}": ${exp.type};\n`;
}
dtsContent += ` }\n\n`;
dtsContent += ` /**\n`;
dtsContent += ` * DataBus interface with type-safe import\n`;
dtsContent += ` */\n`;
dtsContent += ` interface DataBus {\n`;
dtsContent += ` /**\n`;
dtsContent += ` * Export a value\n`;
dtsContent += ` * @template T\n`;
dtsContent += ` * @param {string} name - Export identifier\n`;
dtsContent += ` * @param {T} value - Value to export\n`;
dtsContent += ` */\n`;
dtsContent += ` export<T>(name: string, value: T): void;\n\n`;
dtsContent += ` /**\n`;
dtsContent += ` * Import a previously exported value\n`;
dtsContent += ` * @template T\n`;
dtsContent += ` * @param {string} name - Export identifier\n`;
dtsContent += ` * @returns {T} The exported value\n`;
dtsContent += ` */\n`;
dtsContent += ` import<T extends keyof ExportTypes>(name: T): ExportTypes[T];\n\n`;
dtsContent += ` /**\n`;
dtsContent += ` * Check if an export exists\n`;
dtsContent += ` * @param {string} name - Export identifier\n`;
dtsContent += ` */\n`;
dtsContent += ` hasExport(name: string): boolean;\n\n`;
dtsContent += ` /**\n`;
dtsContent += ` * List all available export names\n`;
dtsContent += ` * @returns {string[]}\n`;
dtsContent += ` */\n`;
dtsContent += ` listExports(): string[];\n`;
dtsContent += ` }\n`;
dtsContent += `}\n\n`;
dtsContent += `export {};\n`;
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputPath, dtsContent, "utf-8");
console.log(`Generated ${outputPath} with ${sortedExports.length} exports`);
// Print exports with types
console.log("\nExports found:");
for (const exp of sortedExports) {
console.log(` ${exp.name}: ${exp.type} (from ${exp.sourceFile})`);
}
}
/**
* Main function
*/
function main() {
console.log("Scanning src/ directory for exports...");
const srcDir = path.join(process.cwd(), "src");
const jsFiles = findJsFiles(srcDir);
console.log(`Found ${jsFiles.length} JavaScript files`);
const allExports = [];
for (const file of jsFiles) {
const exports = collectExportsFromFile(file);
allExports.push(...exports);
}
console.log(`Found ${allExports.length} total exports`);
if (allExports.length === 0) {
console.warn("No exports found. Make sure to use global['name'] = value or dataBus.export('name', value)");
return;
}
const outputPath = path.join(process.cwd(), "types", "DataBus.d.ts");
generateDTS(allExports, outputPath);
console.log("\nDone!");
}
main();

View File

@@ -4,6 +4,11 @@
* Handles C4 use started and activated events
*/
/**
* @type {DataBus}
*/
const dataBus = global["dataBus"];
// ==================== Block Break Event Handler ====================
BlockEvents.broken((event) => {
@@ -15,8 +20,7 @@ BlockEvents.broken((event) => {
}
// Get the toExplosionC4Map from global
/** @type {{[key:string]: boolean | null}} */
const toExplosionC4Map = /** @type {any} */ (global["toExplosionC4Map"]);
const toExplosionC4Map = dataBus.import("toExplosionC4Map");
if (toExplosionC4Map === undefined || toExplosionC4Map === null) {
console.warn("C4 Server: toExplosionC4Map is not available");

View File

@@ -2,14 +2,35 @@ const $TickEvent$PlayerTickEvent = Java.loadClass(
"net.minecraftforge.event.TickEvent$PlayerTickEvent",
);
const C4_EXPLOSION_TIME = 10 * 20; // 7 seconds in ticks
const C4_EXPLOSION_POWER = 128; // Explosion power (TNT is 4)
const C4_USE_TIME = 5 * 20; // 5 seconds in ticks
/**
* C4 explosion time in ticks (7 seconds)
* @type {number}
*/
const C4_EXPLOSION_TIME = 10 * 20;
/**
* C4 explosion power (TNT is 4)
* @type {number}
*/
const C4_EXPLOSION_POWER = 128;
/**
* C4 use time in ticks (5 seconds)
* @type {number}
*/
const C4_USE_TIME = 5 * 20;
/**
* @type {DataBus}
*/
const dataBus = /** @type {any} */ (global["dataBus"]);
// Export constants for server scripts
global["C4_EXPLOSION_TIME"] = C4_EXPLOSION_TIME;
global["C4_EXPLOSION_POWER"] = C4_EXPLOSION_POWER;
global["C4_USE_TIME"] = C4_USE_TIME;
dataBus.export("C4_EXPLOSION_TIME", C4_EXPLOSION_TIME);
dataBus.export("C4_EXPLOSION_POWER", C4_EXPLOSION_POWER);
dataBus.export("C4_USE_TIME", C4_USE_TIME);
// Tolerance for floating point comparison
const ANGLE_TOLERANCE = 0.001;
@@ -30,13 +51,24 @@ let operationKeyMapping;
const lastPlayerInfoMap = {};
// Export for server scripts
global["lastPlayerInfoMap"] = lastPlayerInfoMap;
/**
* @type {{ [key: string]:{
* angle: {x: number, y:number, z:number},
* pos: {x: number, y: number, z: number},
* blockPos: {x: number, y: number, z: number}
* } | undefined}}
*/
dataBus.export("lastPlayerInfoMap", lastPlayerInfoMap);
/**
* @type {{[key:string]: boolean}}
* @type {{[key:string]: boolean | null}}
*/
const toExplosionC4Map = {};
global["toExplosionC4Map"] = toExplosionC4Map;
/**
* @type {{[key:string]: boolean | null}}
*/
dataBus.export("toExplosionC4Map", toExplosionC4Map);
/**
* Helper function to compare floating point numbers with tolerance
@@ -119,9 +151,16 @@ function shouldActivateC4(itemstack, level, player) {
}
// Export for server scripts
global["shouldActivateC4"] = shouldActivateC4;
/**
* @param {Internal.ItemStack} itemstack
* @param {Internal.Level} level
* @param {Internal.Player} player
* @returns {boolean}
*/
dataBus.export("shouldActivateC4", shouldActivateC4);
/**
* Check if C4 use should start
* @param {Internal.Player} player
* @param {Internal.Level} level
* @returns {boolean}
@@ -158,7 +197,12 @@ function shouldStartUseC4(player, level) {
}
// Export for server scripts
global["shouldStartUseC4"] = shouldStartUseC4;
/**
* @param {Internal.Player} player
* @param {Internal.Level} level
* @returns {boolean}
*/
dataBus.export("shouldStartUseC4", shouldStartUseC4);
// ==================== Block Registration ====================

View File

@@ -0,0 +1,62 @@
/**
* Creates a simple data-exchange bus for sharing values across KubeJS scripts.
* Provides export/import functionality similar to TypeScript modules with type hints.
*
* @returns {DataBus}
*/
function createDataBus() {
/**
* @type {Map<string, any>}
*/
const dataMap = new Map();
/**
* @type {DataBus}
*/
const bus = {
/**
* Export a value under a given name.
* @template T
* @param {string} name - Export identifier
* @param {T} value - Value to export
*/
export: function (name, value) {
dataMap.set(name, value);
},
/**
* Import a previously exported value.
* @template T
* @param {string} name - Export identifier
* @returns {T} The exported value
* @throws {Error} If the export does not exist.
*/
import: function (name) {
if (!dataMap.has(name)) {
throw new Error(`DataBus: export "${name}" not found`);
}
return dataMap.get(name);
},
/**
* Check if an export exists.
* @param {string} name - Export identifier
* @returns {boolean}
*/
hasExport: function (name) {
return dataMap.has(name);
},
/**
* List all available export names.
* @returns {string[]}
*/
listExports: function () {
return Array.from(dataMap.keys());
},
};
return bus;
}
global["dataBus"] = createDataBus();

93
types/DataBus.d.ts vendored Normal file
View File

@@ -0,0 +1,93 @@
/**
* Auto-generated DataBus type declarations
* Generated by scripts/AutoExport.js
* Do not edit manually
*/
declare global {
/**
* All exported data types
*/
interface ExportTypes {
/**
* Source: startup_scripts\C4.js
* Type: number
*/
"C4_EXPLOSION_POWER": number;
/**
* Source: startup_scripts\C4.js
* Type: number
*/
"C4_EXPLOSION_TIME": number;
/**
* Source: startup_scripts\C4.js
* Type: number
*/
"C4_USE_TIME": number;
/**
* Source: startup_scripts\DataBus.js
* Type: any
*/
"dataBus": any;
/**
* Source: startup_scripts\EventBus.js
* Type: any
*/
"eventBus": any;
/**
* Source: startup_scripts\C4.js
* Type: { [key: string]:{ angle: {x: number, y:number, z:number}, pos: {x: number, y: number, z: number}, blockPos: {x: number, y: number, z: number} } | undefined}
*/
"lastPlayerInfoMap": { [key: string]:{ angle: {x: number, y:number, z:number}, pos: {x: number, y: number, z: number}, blockPos: {x: number, y: number, z: number} } | undefined};
/**
* Source: startup_scripts\C4.js
* Type: (itemstack: Internal.ItemStack, level: Internal.Level, player: Internal.Player) => boolean
*/
"shouldActivateC4": (itemstack: Internal.ItemStack, level: Internal.Level, player: Internal.Player) => boolean;
/**
* Source: startup_scripts\C4.js
* Type: (player: Internal.Player, level: Internal.Level) => boolean
*/
"shouldStartUseC4": (player: Internal.Player, level: Internal.Level) => boolean;
/**
* Source: startup_scripts\C4.js
* Type: {[key:string]: boolean | null}
*/
"toExplosionC4Map": {[key:string]: boolean | null};
}
/**
* DataBus interface with type-safe import
*/
interface DataBus {
/**
* Export a value
* @template T
* @param {string} name - Export identifier
* @param {T} value - Value to export
*/
export<T>(name: string, value: T): void;
/**
* Import a previously exported value
* @template T
* @param {string} name - Export identifier
* @returns {T} The exported value
*/
import<T extends keyof ExportTypes>(name: T): ExportTypes[T];
/**
* Check if an export exists
* @param {string} name - Export identifier
*/
hasExport(name: string): boolean;
/**
* List all available export names
* @returns {string[]}
*/
listExports(): string[];
}
}
export {};