create-yeow 0.2.121 → 0.2.122
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/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, creat
|
|
|
2
2
|
import { resolve, dirname, basename } from 'path';
|
|
3
3
|
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
+
import { createInterface } from 'readline';
|
|
5
6
|
import https from 'https';
|
|
6
7
|
import { createServer } from 'http';
|
|
7
8
|
import { WebSocketServer } from 'ws';
|
|
@@ -15,9 +16,24 @@ const SERVER = resolve(DEVDIR, 'server');
|
|
|
15
16
|
const WS_PORT = 17368;
|
|
16
17
|
|
|
17
18
|
const YES = process.argv.includes('-y') || process.env.CI === 'true';
|
|
19
|
+
const EULA = process.argv.includes('--eula') || YES;
|
|
18
20
|
const PROXY = process.argv.find(a => a.startsWith('--proxy='))?.split('=').slice(1).join('=');
|
|
19
21
|
const STOP = (() => { const a = process.argv.find(a => a.startsWith('--stop=')); if (!a) return null; const m = a.split('=')[1].match(/^(\d+)(s|m|h)?$/); return m ? parseInt(m[1]) * (m[2] === 'm' ? 60 : m[2] === 'h' ? 3600 : 1) : null; })();
|
|
20
22
|
|
|
23
|
+
// ── AI 工作流参数(headless 模式)─────────────────────────────
|
|
24
|
+
function parseDur(flag, def) {
|
|
25
|
+
const a = process.argv.find(a => a.startsWith(flag));
|
|
26
|
+
if (!a) return def;
|
|
27
|
+
const m = a.split('=')[1].match(/^(\d+)(s|m|h)?$/);
|
|
28
|
+
return m ? parseInt(m[1]) * (m[2] === 'm' ? 60 : m[2] === 'h' ? 3600 : 1) : def;
|
|
29
|
+
}
|
|
30
|
+
const TIMEOUT = parseDur('--timeout=', 120); // 服务器加载超时(秒,默认 2m)
|
|
31
|
+
const WAIT = parseDur('--wait=', 30); // 加载成功后等待(秒,默认 30s)
|
|
32
|
+
const OUTFILE = process.argv.find(a => a.startsWith('--outfile='))?.split('=').slice(1).join('=') || null;
|
|
33
|
+
const KEEP = process.argv.includes('--keep');
|
|
34
|
+
const HEADLESS = process.argv.includes('--eula') || process.argv.includes('--timeout')
|
|
35
|
+
|| process.argv.includes('--wait') || process.argv.includes('--outfile') || KEEP;
|
|
36
|
+
|
|
21
37
|
const cfg = JSON.parse(readFileSync(resolve(ROOT, 'yeow.config.json'), 'utf-8'));
|
|
22
38
|
const RUNTIME = resolve(ROOT, '.yeow', 'assets', 'yeow-runtime-0.1.0.jar');
|
|
23
39
|
|
|
@@ -425,6 +441,8 @@ async function main() {
|
|
|
425
441
|
console.log(`\n${c.b}${c.B} Yeow Dev Server${c.r}\n`);
|
|
426
442
|
if (!existsSync(RUNTIME)) { fail(`Runtime JAR not found: ${RUNTIME}`); process.exit(1); }
|
|
427
443
|
|
|
444
|
+
if (HEADLESS) { await runHeadless(); return; }
|
|
445
|
+
|
|
428
446
|
startWebSocket();
|
|
429
447
|
await ensurePaper();
|
|
430
448
|
mkdirSync(SERVER, { recursive: true });
|
|
@@ -439,4 +457,71 @@ async function main() {
|
|
|
439
457
|
startServer();
|
|
440
458
|
}
|
|
441
459
|
|
|
460
|
+
// ── AI 工作流(headless)─────────────────────────────────────────
|
|
461
|
+
// 适合 AI 代理/CI:--eula 自动接受 → 下载 → 启动 → 检测加载完成 →
|
|
462
|
+
// 等待 --wait 秒后命令自动结束(--keep 保留服务器子进程,日志见 --outfile)。
|
|
463
|
+
async function runHeadless() {
|
|
464
|
+
if (!EULA) {
|
|
465
|
+
fail('AI 模式需要 --eula(自动接受 EULA)');
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
const log = OUTFILE ? createWriteStream(OUTFILE, { flags: 'a' }) : null;
|
|
469
|
+
const out = (line) => { if (log) log.write(line + '\n'); else console.log(line); };
|
|
470
|
+
|
|
471
|
+
info('正在下载/准备服务端…');
|
|
472
|
+
await ensurePaper();
|
|
473
|
+
mkdirSync(SERVER, { recursive: true });
|
|
474
|
+
await initServer();
|
|
475
|
+
serverProps(cfg.dev?.port || 17367);
|
|
476
|
+
buildPlugin();
|
|
477
|
+
removeStaleDevJar();
|
|
478
|
+
copyToYeowDir(resolve(ROOT, 'dist', 'plugins', `${cfg.name}-${cfg.version}.yeow.zip`), 'Plugin (.yeow.zip → plugins/Yeow/)');
|
|
479
|
+
copyToPlugins(RUNTIME, 'Runtime');
|
|
480
|
+
|
|
481
|
+
const jvmArgs = ['-Xmx4G', '-Xms4G', '-Dyeow.dev=true', '-Dyeow.ws.port=' + WS_PORT];
|
|
482
|
+
info(`正在启动 Paper ${PAPER_VERSION}...`);
|
|
483
|
+
proc = spawn('java', [...jvmArgs, '-jar', resolve(SERVER, PAPER_JAR), '--nogui'], { cwd: SERVER, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
484
|
+
info(`Server PID: ${proc.pid}`);
|
|
485
|
+
|
|
486
|
+
let started = false, done = false, waitTimer = null;
|
|
487
|
+
const failTimer = setTimeout(() => {
|
|
488
|
+
if (done) return;
|
|
489
|
+
fail(`服务器在 ${TIMEOUT}s 内未完成加载——请检查网络/依赖下载,或加大超时(--timeout=3m)`);
|
|
490
|
+
killProc();
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}, TIMEOUT * 1000);
|
|
493
|
+
|
|
494
|
+
const onLine = (line) => {
|
|
495
|
+
out(line);
|
|
496
|
+
if (!started && line.includes('Starting org.bukkit.craftbukkit.Main')) {
|
|
497
|
+
started = true;
|
|
498
|
+
info('开始加载(Starting org.bukkit.craftbukkit.Main)');
|
|
499
|
+
}
|
|
500
|
+
if (!done && line.includes('Done (') && line.includes('For help')) {
|
|
501
|
+
done = true;
|
|
502
|
+
clearTimeout(failTimer);
|
|
503
|
+
info(`加载完成——等待 ${WAIT}s 后命令结束${KEEP ? '(--keep 保留服务器进程)' : '(关闭服务器进程)'}…`);
|
|
504
|
+
waitTimer = setTimeout(() => {
|
|
505
|
+
info(`等待结束。日志${OUTFILE ? ':' + OUTFILE : '输出于上方'};PID=${proc.pid}${KEEP ? '(服务器仍在运行,按需 kill)' : ''}`);
|
|
506
|
+
if (log) log.end();
|
|
507
|
+
if (KEEP) process.exit(0);
|
|
508
|
+
killProc();
|
|
509
|
+
process.exit(0);
|
|
510
|
+
}, WAIT * 1000);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
readline.createInterface({ input: proc.stdout }).on('line', onLine);
|
|
514
|
+
if (proc.stderr) readline.createInterface({ input: proc.stderr }).on('line', (l) => out('[err] ' + l));
|
|
515
|
+
|
|
516
|
+
proc.on('exit', (code) => {
|
|
517
|
+
if (!done) fail(`服务器提前退出(code ${code})——见${OUTFILE ? '日志 ' + OUTFILE : '上方输出'}`);
|
|
518
|
+
if (log) log.end();
|
|
519
|
+
process.exit(1);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function killProc() {
|
|
524
|
+
if (proc && !proc.killed) { try { proc.kill('SIGKILL'); } catch {} }
|
|
525
|
+
}
|
|
526
|
+
|
|
442
527
|
main().catch(e => { fail(e.message); process.exit(1); });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
onInit, onLoad, onUnload, registerCommand, eventOn,
|
|
3
|
-
|
|
3
|
+
Location, pdcSet, pdcGet, log,
|
|
4
4
|
} from 'yeow-api';
|
|
5
5
|
|
|
6
6
|
onInit(() => { log.info('Init'); });
|
|
@@ -20,23 +20,26 @@ onLoad(() => {
|
|
|
20
20
|
|
|
21
21
|
registerCommand('back', {
|
|
22
22
|
description: 'Teleport to your death location',
|
|
23
|
+
permission: { node: 'back.use', default: 'all' }, // 声明权限节点:普通玩家默认可用,服主可经权限插件管理
|
|
23
24
|
executor: async (p) => {
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
if (p.sender === 'CONSOLE') return;
|
|
26
|
+
const player = p.sender; // 已确认非 CONSOLE → Player
|
|
27
|
+
const raw = await pdcGet(player.uuid, 'back.deathLocation');
|
|
28
|
+
if (!raw) return player.sendMessage('<red>No death location recorded</red>');
|
|
26
29
|
|
|
27
30
|
const loc = JSON.parse(raw);
|
|
28
|
-
const player = await Player.get(p.sender.uuid);
|
|
29
|
-
if (!player) return;
|
|
30
31
|
await player.teleport(new Location(loc.x, loc.y, loc.z, 0, 0, loc.world));
|
|
31
|
-
|
|
32
|
+
await player.sendMessage('<green>Teleported to death location</green>');
|
|
32
33
|
},
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
// ── /ping ──
|
|
36
37
|
registerCommand('ping', {
|
|
38
|
+
permission: { node: 'ping.use', default: 'all' },
|
|
37
39
|
executor: async (p) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
if (p.sender === 'CONSOLE') { log.info('Ping: console'); return; }
|
|
41
|
+
const player = p.sender; // Player
|
|
42
|
+
await player.sendMessage(`Ping: ${player.ping}ms`);
|
|
40
43
|
},
|
|
41
44
|
});
|
|
42
45
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
onInit, onLoad, onUnload, registerCommand, eventOn,
|
|
3
|
-
|
|
3
|
+
Location, pdcSet, pdcGet, log,
|
|
4
4
|
} from 'yeow-api';
|
|
5
5
|
import type { PlayerDeathEvent } from 'yeow-api';
|
|
6
6
|
|
|
@@ -21,23 +21,26 @@ onLoad(() => {
|
|
|
21
21
|
|
|
22
22
|
registerCommand('back', {
|
|
23
23
|
description: 'Teleport to your death location',
|
|
24
|
+
permission: { node: 'back.use', default: 'all' }, // 声明权限节点:普通玩家默认可用,服主可经权限插件管理
|
|
24
25
|
executor: async (p) => {
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
if (p.sender === 'CONSOLE') return;
|
|
27
|
+
const player = p.sender; // 已确认非 CONSOLE → Player
|
|
28
|
+
const raw = await pdcGet(player.uuid, 'back.deathLocation');
|
|
29
|
+
if (!raw) return player.sendMessage('<red>No death location recorded</red>');
|
|
27
30
|
|
|
28
31
|
const loc = JSON.parse(raw);
|
|
29
|
-
const player = await Player.get(p.sender.uuid);
|
|
30
|
-
if (!player) return;
|
|
31
32
|
await player.teleport(new Location(loc.x, loc.y, loc.z, 0, 0, loc.world));
|
|
32
|
-
|
|
33
|
+
await player.sendMessage('<green>Teleported to death location</green>');
|
|
33
34
|
},
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
// ── /ping ──
|
|
37
38
|
registerCommand('ping', {
|
|
39
|
+
permission: { node: 'ping.use', default: 'all' },
|
|
38
40
|
executor: async (p) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
if (p.sender === 'CONSOLE') { log.info('Ping: console'); return; }
|
|
42
|
+
const player = p.sender; // Player
|
|
43
|
+
await player.sendMessage(`Ping: ${player.ping}ms`);
|
|
41
44
|
},
|
|
42
45
|
});
|
|
43
46
|
|