@yeow/create-yeow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @yeow/create-yeow
2
+
3
+ Scaffold a Yeow plugin project.
4
+
5
+ ```bash
6
+ npm create @yeow/yeow@latest
7
+ ```
8
+
9
+ ## Templates
10
+
11
+ - **JavaScript** — ES2020 + Rspack + Babel
12
+ - **TypeScript** — ES2020 + Rspack + SWC
13
+
14
+ ## Development
15
+
16
+ ```bash
17
+ # From monorepo root, link locally
18
+ cd packages/create-yeow
19
+ npm link
20
+
21
+ # Then use anywhere
22
+ npm create @yeow/yeow
23
+ ```
package/bin/index.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, copyFileSync } from 'fs';
4
+ import { resolve, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { createInterface } from 'readline';
7
+ import { execSync } from 'child_process';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ function prompt(query) {
12
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
13
+ return new Promise((resolve) => {
14
+ rl.question(query, (answer) => { rl.close(); resolve(answer.trim()); });
15
+ });
16
+ }
17
+
18
+ function green(msg) { return `\x1b[32m${msg}\x1b[0m`; }
19
+ function dim(msg) { return `\x1b[2m${msg}\x1b[0m`; }
20
+ function bold(msg) { return `\x1b[1m${msg}\x1b[0m`; }
21
+ function cyan(msg) { return `\x1b[36m${msg}\x1b[0m`; }
22
+ function log(msg) { console.log(` ${msg}`); }
23
+ function logOk(msg) { console.log(` ${green('\u2713')} ${msg}`); }
24
+ function logInfo(msg) { console.log(` ${cyan('\u24D8')} ${msg}`); }
25
+
26
+ function toClassName(str) {
27
+ return str.replace(/[^a-zA-Z0-9]+/g, ' ').split(' ').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
28
+ }
29
+
30
+ async function main() {
31
+ console.log(`\n ${bold('Welcome to Yeow!')} ${dim('v0.1.0')}\n`);
32
+
33
+ const name = await prompt(` ${bold('Project name')}: `) || 'my-yeow-plugin';
34
+ const author = await prompt(` ${bold('Author')}: `);
35
+ const description = await prompt(` ${bold('Description')}: `) || 'A Yeow plugin';
36
+ const lang = await prompt(` ${bold('Language')} ${dim('(JS/TS)')} [JS]: `);
37
+ const useTs = lang.toLowerCase().startsWith('t');
38
+
39
+ const projectDir = resolve(process.cwd(), name);
40
+ const pluginName = toClassName(name);
41
+ const tplDir = resolve(__dirname, '..', 'templates', useTs ? 'typescript' : 'javascript');
42
+
43
+ console.log();
44
+ logInfo(`Scaffolding ${bold(name)}...`);
45
+
46
+ if (!existsSync(projectDir)) mkdirSync(projectDir, { recursive: true });
47
+ cpSync(tplDir, projectDir, { recursive: true });
48
+
49
+ // customize package.json
50
+ const pkgPath = resolve(projectDir, 'package.json');
51
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
52
+ pkg.name = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
53
+ pkg.description = description;
54
+ if (author) pkg.author = author;
55
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + '\n');
56
+ logOk('package.json configured');
57
+
58
+ // customize yeow.config.js
59
+ const configPath = resolve(projectDir, 'yeow.config.js');
60
+ let config = readFileSync(configPath, 'utf-8');
61
+ config = config
62
+ .replace(/name:\s*'.*?'/, `name: '${pluginName}'`)
63
+ .replace(/author:\s*'.*?'/, `author: '${author || 'Unknown'}'`)
64
+ .replace(/description:\s*'.*?'/, `description: '${description}'`)
65
+ .replace(/output:\s*'.*?'/, `output: 'dist/${pluginName}-1.0.0.jar'`);
66
+ writeFileSync(configPath, config);
67
+ logOk('yeow.config.js configured');
68
+
69
+ // copy JARs from @yeow/bukkit-template
70
+ const assetsDir = resolve(projectDir, '.yeow', 'assets');
71
+ if (!existsSync(assetsDir)) mkdirSync(assetsDir, { recursive: true });
72
+
73
+ try {
74
+ const { runtimeJar, templateJar } = await import('@yeow/bukkit-template');
75
+ if (existsSync(templateJar)) {
76
+ copyFileSync(templateJar, resolve(assetsDir, 'yeow-template-1.0.0.jar'));
77
+ logOk('Template JAR copied');
78
+ }
79
+ if (existsSync(runtimeJar)) {
80
+ copyFileSync(runtimeJar, resolve(assetsDir, 'yeow-runtime-1.0.0.jar'));
81
+ logOk('Runtime JAR copied');
82
+ }
83
+ } catch {
84
+ logInfo('@yeow/bukkit-template not resolved, JARs will need manual copy');
85
+ }
86
+
87
+ // npm install
88
+ logInfo('Installing dependencies...');
89
+ try {
90
+ execSync('npm install', { cwd: projectDir, stdio: 'pipe' });
91
+ logOk('Dependencies installed');
92
+ } catch {
93
+ logInfo('npm install failed, run manually: cd ' + name + ' && npm install');
94
+ }
95
+
96
+ console.log(`\n ${green('\u2713')} ${bold('Done!')}\n`);
97
+ console.log(` ${dim('$')} ${cyan('cd ' + name)}`);
98
+ console.log(` ${dim('$')} ${cyan('npm run dev')} ${dim('Start development server')}`);
99
+ console.log(` ${dim('$')} ${cyan('npm run build')} ${dim('Build plugin JAR')}\n`);
100
+ }
101
+
102
+ main().catch((err) => { console.error(err); process.exit(1); });
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@yeow/create-yeow",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a Yeow plugin project",
5
+ "bin": {
6
+ "create-yeow": "./bin/index.js"
7
+ },
8
+ "type": "module",
9
+ "dependencies": {
10
+ "@yeow/bukkit-template": "0.1.0"
11
+ },
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "license": "MIT"
16
+ }
@@ -0,0 +1,24 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { execSync } = require('child_process');
4
+ const AdmZip = require('adm-zip');
5
+ const configPath = path.resolve('yeow.config.js');
6
+ function loadConfig() { if (!fs.existsSync(configPath)) { console.error('yeow.config.js not found'); process.exit(1); } return require(configPath); }
7
+ function compileUserCode() { console.log('Compiling user code with Rspack...'); try { execSync('npx rspack build', { stdio: 'inherit' }); console.log('User code compiled'); } catch (error) { console.error('Compilation failed:', error.message); process.exit(1); } }
8
+ function buildPlugin() {
9
+ const config = loadConfig(); const { plugin, build } = config;
10
+ console.log('\nBuilding Yeow plugin...'); console.log(' ' + plugin.name + ' v' + plugin.version);
11
+ compileUserCode();
12
+ const templateJarPath = path.resolve(build.templateJar);
13
+ if (!fs.existsSync(templateJarPath)) { console.error('Template JAR not found:', templateJarPath); process.exit(1); }
14
+ const templateZip = new AdmZip(templateJarPath);
15
+ templateZip.updateFile('plugin.yml', Buffer.from('name: ' + plugin.name + '\nversion: ' + plugin.version + '\nmain: yeow.template.YeowPluginBootstrap\napi-version: \"1.21\"\ndepend:\n - Yeow-Runtime\nauthor: ' + (plugin.author || 'Unknown') + '\ndescription: ' + (plugin.description || 'A Yeow plugin') + '\n'));
16
+ const userCodePath = path.resolve('dist/.yeow/main.js');
17
+ if (fs.existsSync(userCodePath)) { templateZip.addFile('.yeow/main.js', fs.readFileSync(userCodePath)); } else { console.error('Compiled user code not found'); process.exit(1); }
18
+ const outputPath = build.output || 'dist/plugin.jar';
19
+ const outputDir = path.dirname(outputPath);
20
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
21
+ templateZip.writeZip(outputPath);
22
+ console.log('Plugin built successfully!'); console.log(' Output: ' + outputPath); console.log(' Size: ' + (fs.statSync(outputPath).size / 1024).toFixed(2) + ' KB');
23
+ }
24
+ buildPlugin();
@@ -0,0 +1,135 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { spawn, execSync } = require('child_process');
4
+ const readline = require('readline');
5
+
6
+ const DEV_DIR = path.resolve(__dirname, 'dev');
7
+ const CACHE_DIR = path.join(DEV_DIR, 'cache');
8
+ const SERVER_DIR = path.join(DEV_DIR, 'server');
9
+ const PAPER_VERSION = '1.21.4';
10
+ const PAPER_BUILD = 232;
11
+ const PAPER_FILENAME = 'paper-' + PAPER_VERSION + '-' + PAPER_BUILD + '.jar';
12
+ const PAPER_URL = 'https://api.papermc.io/v2/projects/paper/versions/' + PAPER_VERSION + '/builds/' + PAPER_BUILD + '/downloads/' + PAPER_FILENAME;
13
+ const PAPER_CACHE_PATH = path.join(CACHE_DIR, PAPER_FILENAME);
14
+ const DOUBLE_CTRL_C_WINDOW = 3000;
15
+ const YEOW_CONFIG_PATH = path.resolve('yeow.config.js');
16
+ const c = { reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', red: '\x1b[31m', ok: '\x1b[32m\u2713\x1b[0m', fail: '\x1b[31m\u2717\x1b[0m', info: '\x1b[36m\u24D8\x1b[0m', warn: '\x1b[33m\u26A0\x1b[0m' };
17
+ let serverProcess = null, ctrlCPressCount = 0, ctrlCTimer = null, srcWatcher = null, isCompiling = false;
18
+ function log(msg, color) { if (!color) color = ''; console.log(c.dim + '[' + new Date().toLocaleTimeString() + ']' + c.reset + ' ' + color + msg + c.reset); }
19
+ function logOk(msg) { log(c.ok + ' ' + msg, c.green); }
20
+ function logFail(msg) { log(c.fail + ' ' + msg, c.red); }
21
+ function logWarn(msg) { log(c.warn + ' ' + msg, c.yellow); }
22
+ function logInfo(msg) { log(c.info + ' ' + msg, c.cyan); }
23
+ function logBright(msg) { log(msg, c.bold); }
24
+
25
+ async function ensurePaperJar(devConfig) {
26
+ if (fs.existsSync(PAPER_CACHE_PATH) && fs.statSync(PAPER_CACHE_PATH).size > 1000000) { logOk('Paper jar found in cache'); return; }
27
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
28
+ const proxy = await new Promise(function(r) { rl.question(' Proxy address [blank to skip]: ', function(v) { rl.close(); r(v.trim()); }); });
29
+ if (proxy) { devConfig.proxy = proxy; var cfg = require(YEOW_CONFIG_PATH); cfg.dev = cfg.dev || {}; cfg.dev.proxy = proxy; fs.writeFileSync(YEOW_CONFIG_PATH, 'module.exports = ' + JSON.stringify(cfg, null, 4) + ';\n'); }
30
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
31
+ logInfo('Downloading Paper ' + PAPER_VERSION + '...');
32
+ var agent = null;
33
+ if (proxy) { try { var m = await import('https-proxy-agent'); agent = new m.HttpsProxyAgent(proxy); } catch(e) {} }
34
+ var done = false;
35
+ try {
36
+ await new Promise(function(resolve, reject) {
37
+ var file = fs.createWriteStream(PAPER_CACHE_PATH);
38
+ var opts = {}; if (agent) opts.agent = agent;
39
+ var req = require('https').get(PAPER_URL, opts, function(res) {
40
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { file.close(); fs.unlinkSync(PAPER_CACHE_PATH); return reject(new Error('Redirect')); }
41
+ if (res.statusCode !== 200) { file.close(); fs.unlinkSync(PAPER_CACHE_PATH); return reject(new Error('HTTP ' + res.statusCode)); }
42
+ res.pipe(file);
43
+ file.on('finish', function() { file.close(); resolve(); });
44
+ });
45
+ req.on('error', function(err) { file.close(); if (fs.existsSync(PAPER_CACHE_PATH)) fs.unlinkSync(PAPER_CACHE_PATH); reject(err); });
46
+ });
47
+ logOk('Paper jar downloaded');
48
+ } catch(err) {
49
+ logFail('Download failed: ' + err.message);
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ async function ensureRuntimeJar(devConfig) {
55
+ var p = path.resolve(devConfig.runtimeJar);
56
+ if (fs.existsSync(p) && fs.statSync(p).size > 1000) return p;
57
+ logFail('Yeow-Runtime.jar not found at ' + p); process.exit(1);
58
+ }
59
+
60
+ function initServer(devConfig) {
61
+ var paperJar = path.join(SERVER_DIR, PAPER_FILENAME);
62
+ if (!fs.existsSync(paperJar)) fs.copyFileSync(PAPER_CACHE_PATH, paperJar);
63
+ var eulaPath = path.join(SERVER_DIR, 'eula.txt');
64
+ if (fs.existsSync(eulaPath) && fs.readFileSync(eulaPath, 'utf-8').includes('eula=true')) return;
65
+ var propsPath = path.join(SERVER_DIR, 'server.properties');
66
+ if (!fs.existsSync(propsPath)) {
67
+ logInfo('Initializing server...');
68
+ var p = spawn(devConfig.javaPath, ['-Xmx' + devConfig.memory, '-Xms' + devConfig.memory, '-jar', paperJar, '--nogui'], { cwd: SERVER_DIR, stdio: 'pipe' });
69
+ var t = setTimeout(function() { if (!p.killed) p.kill(); }, 15000);
70
+ p.on('exit', function() { clearTimeout(t); });
71
+ p.on('error', function() { clearTimeout(t); });
72
+ }
73
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
74
+ rl.question(' Press Enter to accept EULA... ', function() { rl.close(); fs.writeFileSync(eulaPath, 'eula=true\n'); logOk('EULA accepted'); });
75
+ }
76
+
77
+ function getServerProperties(port) {
78
+ var propsPath = path.join(SERVER_DIR, 'server.properties');
79
+ var merged = {};
80
+ if (fs.existsSync(propsPath)) {
81
+ fs.readFileSync(propsPath, 'utf-8').split('\n').forEach(function(line) {
82
+ line = line.trim(); if (line && !line.startsWith('#')) { var i = line.indexOf('='); if (i > 0) merged[line.substring(0, i).trim()] = line.substring(i + 1).trim(); }
83
+ });
84
+ }
85
+ merged.difficulty = 'easy'; merged['enable-command-block'] = 'true'; merged['max-players'] = '10'; merged.motd = 'A Yeow Dev Server'; merged['online-mode'] = 'false'; merged['server-port'] = String(port); merged['spawn-protection'] = '0';
86
+ var out = '#Minecraft server properties\n';
87
+ for (var k in merged) out += k + '=' + merged[k] + '\n'; fs.writeFileSync(propsPath, out);
88
+ }
89
+
90
+ function buildDevPlugin() {
91
+ logInfo('Building plugin...');
92
+ try { execSync('node .yeow/build.js', { stdio: 'inherit', cwd: path.resolve(__dirname, '..') }); logOk('Plugin built'); }
93
+ catch (e) { logFail('Build failed: ' + e.message); process.exit(1); }
94
+ }
95
+
96
+ function copyToPlugins(src, label) {
97
+ var dir = path.join(SERVER_DIR, 'plugins'); fs.mkdirSync(dir, { recursive: true });
98
+ var dest = path.join(dir, path.basename(src)); if (fs.existsSync(dest)) fs.unlinkSync(dest); fs.copyFileSync(src, dest); logOk(label + ' copied');
99
+ }
100
+
101
+ function startServer(devConfig) {
102
+ var paperJar = path.join(SERVER_DIR, PAPER_FILENAME);
103
+ logBright('\nStarting server (type commands to send)...\n');
104
+ serverProcess = spawn(devConfig.javaPath, ['-Xmx' + devConfig.memory, '-Xms' + devConfig.memory, '-Dyeow.dev=true', '-jar', paperJar, '--nogui'], { cwd: SERVER_DIR, stdio: ['pipe', 'inherit', 'inherit'] });
105
+ serverProcess.on('exit', function(code) { logWarn('Server exited with code ' + code); process.exit(0); });
106
+ serverProcess.on('error', function(err) { logFail('Failed: ' + err.message); process.exit(1); });
107
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
108
+ rl.on('line', function(line) { if (serverProcess && !serverProcess.killed && line.trim()) serverProcess.stdin.write(line.trim() + '\n'); });
109
+ }
110
+
111
+ process.on('SIGINT', function() {
112
+ ctrlCPressCount++;
113
+ if (ctrlCPressCount >= 2) { if (serverProcess && !serverProcess.killed) serverProcess.kill(); process.exit(0); }
114
+ if (serverProcess && !serverProcess.killed) serverProcess.stdin.write('stop\n');
115
+ if (ctrlCTimer) clearTimeout(ctrlCTimer);
116
+ ctrlCTimer = setTimeout(function() { ctrlCPressCount = 0; }, DOUBLE_CTRL_C_WINDOW);
117
+ });
118
+
119
+ async function main() {
120
+ var yeowConfig = require(YEOW_CONFIG_PATH);
121
+ var devConfig = yeowConfig.dev || {};
122
+ devConfig.port = devConfig.port || 17367; devConfig.memory = devConfig.memory || '8G'; devConfig.javaPath = devConfig.javaPath || 'java'; devConfig.runtimeJar = devConfig.runtimeJar || '.yeow/assets/yeow-runtime-1.0.0.jar';
123
+ console.log('\n' + c.bold + c.blue + ' Yeow Dev Server' + c.reset + '\n ' + c.dim + '----------------------------------------' + c.reset + '\n Port: ' + c.bold + devConfig.port + c.reset + '\n Memory: ' + c.bold + devConfig.memory + c.reset + '\n');
124
+ var runtimeJarPath = await ensureRuntimeJar(devConfig);
125
+ await ensurePaperJar(devConfig);
126
+ fs.mkdirSync(SERVER_DIR, { recursive: true });
127
+ initServer(devConfig);
128
+ getServerProperties(devConfig.port);
129
+ buildDevPlugin();
130
+ copyToPlugins(path.resolve(yeowConfig.build.output || 'dist/plugin.jar'), 'Plugin');
131
+ copyToPlugins(runtimeJarPath, 'Yeow-Runtime');
132
+ startServer(devConfig);
133
+ }
134
+
135
+ main().catch(function(err) { logFail(err.message); process.exit(1); });
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "my-yeow-plugin",
3
+ "version": "1.0.0",
4
+ "description": "A Yeow plugin",
5
+ "scripts": {
6
+ "build": "node .yeow/build.js",
7
+ "dev": "node .yeow/dev-server.js"
8
+ },
9
+ "devDependencies": {
10
+ "@babel/core": "^7.24.0",
11
+ "@babel/preset-env": "^7.24.0",
12
+ "@rspack/cli": "^1.0.0",
13
+ "@rspack/core": "^1.0.0",
14
+ "adm-zip": "^0.5.10",
15
+ "babel-loader": "^9.1.0",
16
+ "core-js": "^3.36.0",
17
+ "https-proxy-agent": "^9.0.0"
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ const path = require('path');
2
+ const { defineConfig } = require('@rspack/cli');
3
+ module.exports = defineConfig({
4
+ target: ['web', 'es2020'],
5
+ entry: { main: './src/index.js' },
6
+ output: { path: path.resolve(__dirname, 'dist/.yeow'), filename: 'main.js', library: { type: 'commonjs2' }, clean: true },
7
+ module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', { targets: { esmodules: true }, modules: 'cjs', useBuiltIns: false, corejs: false }]] } } }] },
8
+ optimization: { minimize: true }
9
+ });
@@ -0,0 +1,40 @@
1
+ yeow.command.register('hello', {
2
+ description: 'Say hello',
3
+ usage: '/hello [name]',
4
+ executor(params) {
5
+ const name = params.args[0] || 'World';
6
+ params.sender.sendMessage(`Hello, ${name}!`);
7
+ console.log(`${params.sender.name} ran /hello`);
8
+ }
9
+ });
10
+
11
+ yeow.command.register('playerinfo', {
12
+ description: 'Show your player info',
13
+ usage: '/playerinfo',
14
+ executor(params) {
15
+ const player = params.sender;
16
+ player.sendMessage(`Name: ${player.name}`);
17
+ player.sendMessage(`UUID: ${player.uuid}`);
18
+ player.sendMessage(`Ping: ${player.ping}ms`);
19
+ player.sendMessage(`Health: ${player.health}/${player.maxHealth}`);
20
+ player.sendMessage(`Food: ${player.foodLevel}`);
21
+ player.sendMessage(`Gamemode: ${player.gamemode}`);
22
+ player.sendMessage(`OP: ${player.isOp}`);
23
+ }
24
+ });
25
+
26
+ yeow.command.register('setblock2', {
27
+ description: 'Set a block',
28
+ usage: '/setblock2 <x> <y> <z> <type>',
29
+ executor(params) {
30
+ const [x, y, z, type] = params.args;
31
+ if (!x || !y || !z || !type) {
32
+ params.sender.sendMessage('Usage: /setblock2 <x> <y> <z> <type>');
33
+ return;
34
+ }
35
+ const world = yeow.world.get('world');
36
+ if (!world) { params.sender.sendMessage('World not found'); return; }
37
+ world.setBlock(parseInt(x), parseInt(y), parseInt(z), type);
38
+ params.sender.sendMessage(`Set block at ${x} ${y} ${z} to ${type}`);
39
+ }
40
+ });
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ plugin: {
3
+ name: 'MyPlugin', version: '1.0.0', author: 'Unknown',
4
+ description: 'A Yeow plugin'
5
+ },
6
+ build: {
7
+ entry: 'src/index.js',
8
+ output: 'dist/MyPlugin-1.0.0.jar',
9
+ templateJar: '.yeow/assets/yeow-template-1.0.0.jar'
10
+ },
11
+ dev: {
12
+ port: 17367, memory: '8G', javaPath: 'java',
13
+ runtimeJar: '.yeow/assets/yeow-runtime-1.0.0.jar', proxy: ''
14
+ }
15
+ };
@@ -0,0 +1,24 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { execSync } = require('child_process');
4
+ const AdmZip = require('adm-zip');
5
+ const configPath = path.resolve('yeow.config.js');
6
+ function loadConfig() { if (!fs.existsSync(configPath)) { console.error('yeow.config.js not found'); process.exit(1); } return require(configPath); }
7
+ function compileUserCode() { console.log('Compiling user code with Rspack...'); try { execSync('npx rspack build', { stdio: 'inherit' }); console.log('User code compiled'); } catch (error) { console.error('Compilation failed:', error.message); process.exit(1); } }
8
+ function buildPlugin() {
9
+ const config = loadConfig(); const { plugin, build } = config;
10
+ console.log('\nBuilding Yeow plugin...'); console.log(' ' + plugin.name + ' v' + plugin.version);
11
+ compileUserCode();
12
+ const templateJarPath = path.resolve(build.templateJar);
13
+ if (!fs.existsSync(templateJarPath)) { console.error('Template JAR not found:', templateJarPath); process.exit(1); }
14
+ const templateZip = new AdmZip(templateJarPath);
15
+ templateZip.updateFile('plugin.yml', Buffer.from('name: ' + plugin.name + '\nversion: ' + plugin.version + '\nmain: yeow.template.YeowPluginBootstrap\napi-version: "1.21"\ndepend:\n - Yeow-Runtime\nauthor: ' + (plugin.author || 'Unknown') + '\ndescription: ' + (plugin.description || 'A Yeow plugin') + '\n'));
16
+ const userCodePath = path.resolve('dist/.yeow/main.js');
17
+ if (fs.existsSync(userCodePath)) { templateZip.addFile('.yeow/main.js', fs.readFileSync(userCodePath)); } else { console.error('Compiled user code not found'); process.exit(1); }
18
+ const outputPath = build.output || 'dist/plugin.jar';
19
+ const outputDir = path.dirname(outputPath);
20
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
21
+ templateZip.writeZip(outputPath);
22
+ console.log('Plugin built successfully!'); console.log(' Output: ' + outputPath); console.log(' Size: ' + (fs.statSync(outputPath).size / 1024).toFixed(2) + ' KB');
23
+ }
24
+ buildPlugin();
@@ -0,0 +1,153 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { spawn, execSync } = require('child_process');
4
+ const readline = require('readline');
5
+
6
+ const DEV_DIR = path.resolve(__dirname, 'dev');
7
+ const CACHE_DIR = path.join(DEV_DIR, 'cache');
8
+ const SERVER_DIR = path.join(DEV_DIR, 'server');
9
+ const PAPER_VERSION = '1.21.4';
10
+ const PAPER_BUILD = 232;
11
+ const PAPER_FILENAME = 'paper-' + PAPER_VERSION + '-' + PAPER_BUILD + '.jar';
12
+ const PAPER_URL = 'https://api.papermc.io/v2/projects/paper/versions/' + PAPER_VERSION + '/builds/' + PAPER_BUILD + '/downloads/' + PAPER_FILENAME;
13
+ const PAPER_CACHE_PATH = path.join(CACHE_DIR, PAPER_FILENAME);
14
+ const DOUBLE_CTRL_C_WINDOW = 3000;
15
+ const YEOW_CONFIG_PATH = path.resolve('yeow.config.js');
16
+ const c = { reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', red: '\x1b[31m', ok: '\x1b[32m\u2713\x1b[0m', fail: '\x1b[31m\u2717\x1b[0m', info: '\x1b[36m\u24D8\x1b[0m', warn: '\x1b[33m\u26A0\x1b[0m' };
17
+ let serverProcess = null, ctrlCPressCount = 0, ctrlCTimer = null, srcWatcher = null, isCompiling = false;
18
+ function log(msg, color) { if (!color) color = ''; console.log(c.dim + '[' + new Date().toLocaleTimeString() + ']' + c.reset + ' ' + color + msg + c.reset); }
19
+ function logOk(msg) { log(c.ok + ' ' + msg, c.green); }
20
+ function logFail(msg) { log(c.fail + ' ' + msg, c.red); }
21
+ function logWarn(msg) { log(c.warn + ' ' + msg, c.yellow); }
22
+ function logInfo(msg) { log(c.info + ' ' + msg, c.cyan); }
23
+ function logBright(msg) { log(msg, c.bold); }
24
+
25
+ async function ensurePaperJar(devConfig) {
26
+ if (fs.existsSync(PAPER_CACHE_PATH) && fs.statSync(PAPER_CACHE_PATH).size > 1000000) { logOk('Paper jar found in cache'); return; }
27
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
28
+ const proxy = await new Promise(function(r) { rl.question(' Proxy address [blank to skip]: ', function(v) { rl.close(); r(v.trim()); }); });
29
+ if (proxy) { devConfig.proxy = proxy; var cfg = require(YEOW_CONFIG_PATH); cfg.dev = cfg.dev || {}; cfg.dev.proxy = proxy; fs.writeFileSync(YEOW_CONFIG_PATH, 'module.exports = ' + JSON.stringify(cfg, null, 4) + ';\n'); }
30
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
31
+ logInfo('Downloading Paper ' + PAPER_VERSION + '...');
32
+ var agent = null;
33
+ if (proxy) { try { var m = await import('https-proxy-agent'); agent = new m.HttpsProxyAgent(proxy); } catch(e) {} }
34
+ try {
35
+ await new Promise(function(resolve, reject) {
36
+ var file = fs.createWriteStream(PAPER_CACHE_PATH);
37
+ var opts = {}; if (agent) opts.agent = agent;
38
+ var req = require('https').get(PAPER_URL, opts, function(res) {
39
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { file.close(); fs.unlinkSync(PAPER_CACHE_PATH); return reject(new Error('Redirect')); }
40
+ if (res.statusCode !== 200) { file.close(); fs.unlinkSync(PAPER_CACHE_PATH); return reject(new Error('HTTP ' + res.statusCode)); }
41
+ res.pipe(file);
42
+ file.on('finish', function() { file.close(); resolve(); });
43
+ });
44
+ req.on('error', function(err) { file.close(); if (fs.existsSync(PAPER_CACHE_PATH)) fs.unlinkSync(PAPER_CACHE_PATH); reject(err); });
45
+ });
46
+ logOk('Paper jar downloaded');
47
+ } catch(err) {
48
+ logFail('Download failed: ' + err.message);
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ async function ensureRuntimeJar(devConfig) {
54
+ var p = path.resolve(devConfig.runtimeJar);
55
+ if (fs.existsSync(p) && fs.statSync(p).size > 1000) return p;
56
+ logFail('Yeow-Runtime.jar not found at ' + p); process.exit(1);
57
+ }
58
+
59
+ function initServer(devConfig) {
60
+ var paperJar = path.join(SERVER_DIR, PAPER_FILENAME);
61
+ if (!fs.existsSync(paperJar)) fs.copyFileSync(PAPER_CACHE_PATH, paperJar);
62
+ var eulaPath = path.join(SERVER_DIR, 'eula.txt');
63
+ if (fs.existsSync(eulaPath) && fs.readFileSync(eulaPath, 'utf-8').includes('eula=true')) return;
64
+ var propsPath = path.join(SERVER_DIR, 'server.properties');
65
+ if (!fs.existsSync(propsPath)) {
66
+ logInfo('Initializing server...');
67
+ var p = spawn(devConfig.javaPath, ['-Xmx' + devConfig.memory, '-Xms' + devConfig.memory, '-jar', paperJar, '--nogui'], { cwd: SERVER_DIR, stdio: 'pipe' });
68
+ var t = setTimeout(function() { if (!p.killed) p.kill(); }, 15000);
69
+ p.on('exit', function() { clearTimeout(t); });
70
+ p.on('error', function() { clearTimeout(t); });
71
+ }
72
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
73
+ rl.question(' Press Enter to accept EULA... ', function() { rl.close(); fs.writeFileSync(eulaPath, 'eula=true\n'); logOk('EULA accepted'); });
74
+ }
75
+
76
+ function getServerProperties(port) {
77
+ var propsPath = path.join(SERVER_DIR, 'server.properties');
78
+ var merged = {};
79
+ if (fs.existsSync(propsPath)) {
80
+ fs.readFileSync(propsPath, 'utf-8').split('\n').forEach(function(line) {
81
+ line = line.trim(); if (line && !line.startsWith('#')) { var i = line.indexOf('='); if (i > 0) merged[line.substring(0, i).trim()] = line.substring(i + 1).trim(); }
82
+ });
83
+ }
84
+ merged.difficulty = 'easy'; merged['enable-command-block'] = 'true'; merged['max-players'] = '10'; merged.motd = 'A Yeow Dev Server'; merged['online-mode'] = 'false'; merged['server-port'] = String(port); merged['spawn-protection'] = '0';
85
+ var out = '#Minecraft server properties\n';
86
+ for (var k in merged) out += k + '=' + merged[k] + '\n'; fs.writeFileSync(propsPath, out);
87
+ }
88
+
89
+ function buildDevPlugin() {
90
+ logInfo('Building plugin...');
91
+ try { execSync('node .yeow/build.js', { stdio: 'inherit', cwd: path.resolve(__dirname, '..') }); logOk('Plugin built'); }
92
+ catch (e) { logFail('Build failed: ' + e.message); process.exit(1); }
93
+ }
94
+
95
+ function copyToPlugins(src, label) {
96
+ var dir = path.join(SERVER_DIR, 'plugins'); fs.mkdirSync(dir, { recursive: true });
97
+ var dest = path.join(dir, path.basename(src)); if (fs.existsSync(dest)) fs.unlinkSync(dest); fs.copyFileSync(src, dest); logOk(label + ' copied');
98
+ }
99
+
100
+ function startHotReloadWatcher() {
101
+ const srcDir = path.resolve(__dirname, '../src');
102
+ if (!fs.existsSync(srcDir)) return;
103
+ let debounceTimer = null;
104
+ srcWatcher = fs.watch(srcDir, { recursive: true }, function(eventType, filename) {
105
+ if (!filename || !/\.ts$/.test(filename) || isCompiling) return;
106
+ if (debounceTimer) clearTimeout(debounceTimer);
107
+ debounceTimer = setTimeout(function() {
108
+ isCompiling = true;
109
+ logInfo('Source changed, rebuilding...');
110
+ try { execSync('node .yeow/build.js', { stdio: 'pipe', cwd: path.resolve(__dirname, '..') }); logOk('Hot reload file updated'); }
111
+ catch (e) { logFail('Build failed: ' + e.message); }
112
+ isCompiling = false;
113
+ }, 1000);
114
+ });
115
+ logInfo('Watching src/ for changes (hot reload)...');
116
+ }
117
+
118
+ function startServer(devConfig) {
119
+ var paperJar = path.join(SERVER_DIR, PAPER_FILENAME);
120
+ logBright('\nStarting server (type commands to send)...\n');
121
+ serverProcess = spawn(devConfig.javaPath, ['-Xmx' + devConfig.memory, '-Xms' + devConfig.memory, '-Dyeow.dev=true', '-jar', paperJar, '--nogui'], { cwd: SERVER_DIR, stdio: ['pipe', 'inherit', 'inherit'] });
122
+ serverProcess.on('exit', function(code) { logWarn('Server exited with code ' + code); process.exit(0); });
123
+ serverProcess.on('error', function(err) { logFail('Failed: ' + err.message); process.exit(1); });
124
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
125
+ rl.on('line', function(line) { if (serverProcess && !serverProcess.killed && line.trim()) serverProcess.stdin.write(line.trim() + '\n'); });
126
+ }
127
+
128
+ process.on('SIGINT', function() {
129
+ ctrlCPressCount++;
130
+ if (ctrlCPressCount >= 2) { if (serverProcess && !serverProcess.killed) serverProcess.kill(); process.exit(0); }
131
+ if (serverProcess && !serverProcess.killed) serverProcess.stdin.write('stop\n');
132
+ if (ctrlCTimer) clearTimeout(ctrlCTimer);
133
+ ctrlCTimer = setTimeout(function() { ctrlCPressCount = 0; }, DOUBLE_CTRL_C_WINDOW);
134
+ });
135
+
136
+ async function main() {
137
+ var yeowConfig = require(YEOW_CONFIG_PATH);
138
+ var devConfig = yeowConfig.dev || {};
139
+ devConfig.port = devConfig.port || 17367; devConfig.memory = devConfig.memory || '8G'; devConfig.javaPath = devConfig.javaPath || 'java'; devConfig.runtimeJar = devConfig.runtimeJar || '.yeow/assets/yeow-runtime-1.0.0.jar';
140
+ console.log('\n' + c.bold + c.blue + ' Yeow Dev Server' + c.reset + '\n ' + c.dim + '----------------------------------------' + c.reset + '\n Port: ' + c.bold + devConfig.port + c.reset + '\n Memory: ' + c.bold + devConfig.memory + c.reset + '\n');
141
+ var runtimeJarPath = await ensureRuntimeJar(devConfig);
142
+ await ensurePaperJar(devConfig);
143
+ fs.mkdirSync(SERVER_DIR, { recursive: true });
144
+ initServer(devConfig);
145
+ getServerProperties(devConfig.port);
146
+ buildDevPlugin();
147
+ copyToPlugins(path.resolve(yeowConfig.build.output || 'dist/plugin.jar'), 'Plugin');
148
+ copyToPlugins(runtimeJarPath, 'Yeow-Runtime');
149
+ startHotReloadWatcher();
150
+ startServer(devConfig);
151
+ }
152
+
153
+ main().catch(function(err) { logFail(err.message); process.exit(1); });
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "my-yeow-plugin",
3
+ "version": "1.0.0",
4
+ "description": "A Yeow plugin",
5
+ "scripts": {
6
+ "build": "node .yeow/build.js",
7
+ "dev": "node .yeow/dev-server.js"
8
+ },
9
+ "devDependencies": {
10
+ "@rspack/cli": "^1.0.0",
11
+ "@rspack/core": "^1.0.0",
12
+ "adm-zip": "^0.5.10",
13
+ "https-proxy-agent": "^9.0.0"
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ const path = require('path');
2
+ const { defineConfig } = require('@rspack/cli');
3
+ module.exports = defineConfig({
4
+ target: ['web', 'es2020'],
5
+ entry: { main: './src/index.ts' },
6
+ output: { path: path.resolve(__dirname, 'dist/.yeow'), filename: 'main.js', library: { type: 'commonjs2' }, clean: true },
7
+ module: { rules: [{ test: /\.ts$/, exclude: /node_modules/, loader: 'builtin:swc-loader', options: { jsc: { parser: { syntax: 'typescript' } } } }] },
8
+ optimization: { minimize: true }
9
+ });
@@ -0,0 +1,24 @@
1
+ yeow.command.register('hello', {
2
+ description: 'Say hello',
3
+ usage: '/hello [name]',
4
+ executor(params) {
5
+ const name = params.args[0] || 'World';
6
+ params.sender.sendMessage('Hello, ' + name + '!');
7
+ console.log(params.sender.name + ' ran /hello');
8
+ }
9
+ });
10
+
11
+ yeow.command.register('playerinfo', {
12
+ description: 'Show your player info',
13
+ usage: '/playerinfo',
14
+ executor(params) {
15
+ const p = params.sender;
16
+ p.sendMessage('Name: ' + p.name);
17
+ p.sendMessage('UUID: ' + p.uuid);
18
+ p.sendMessage('Ping: ' + p.ping + 'ms');
19
+ p.sendMessage('Health: ' + p.health + '/' + p.maxHealth);
20
+ p.sendMessage('Food: ' + p.foodLevel);
21
+ p.sendMessage('Gamemode: ' + p.gamemode);
22
+ p.sendMessage('OP: ' + p.isOp);
23
+ }
24
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "moduleResolution": "node",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "./dist/ts",
10
+ "noEmit": true
11
+ },
12
+ "include": ["src/**/*.ts"]
13
+ }
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ plugin: {
3
+ name: 'MyPlugin', version: '1.0.0', author: 'Unknown',
4
+ description: 'A Yeow plugin'
5
+ },
6
+ build: {
7
+ entry: 'src/index.ts',
8
+ output: 'dist/MyPlugin-1.0.0.jar',
9
+ templateJar: '.yeow/assets/yeow-template-1.0.0.jar'
10
+ },
11
+ dev: {
12
+ port: 17367, memory: '8G', javaPath: 'java',
13
+ runtimeJar: '.yeow/assets/yeow-runtime-1.0.0.jar', proxy: ''
14
+ }
15
+ };