create-yeow 0.2.64 → 0.2.65
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 +1 -1
- package/templates/default/.yeow/assets/yeow-runtime-0.1.0.jar +0 -0
- package/templates/default/.yeow/dev-server.js +106 -30
- package/templates/default/package.json +1 -1
- package/templates/default/src/index.js +51 -23
- package/templates/default/src/index.ts +52 -24
- package/templates/default/.yeow/assets/libquickjs-java-wrapper.dll +0 -0
package/package.json
CHANGED
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, createWriteStream, statSync, watch, readdirSync } from 'fs';
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, createWriteStream, statSync, watch, readdirSync, rmSync } from 'fs';
|
|
2
2
|
import { resolve, dirname, basename } from 'path';
|
|
3
3
|
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
@@ -12,11 +12,6 @@ const ROOT = resolve(__dirname, '..');
|
|
|
12
12
|
const DEVDIR = resolve(ROOT, '.yeow', 'dev');
|
|
13
13
|
const CACHE = resolve(DEVDIR, 'cache');
|
|
14
14
|
const SERVER = resolve(DEVDIR, 'server');
|
|
15
|
-
const PAPER = '1.21.4';
|
|
16
|
-
const BUILD = '232';
|
|
17
|
-
const JAR = `paper-${PAPER}-${BUILD}.jar`;
|
|
18
|
-
const URL = `https://fill-data.papermc.io/v1/objects/5ee4f542f628a14c644410b08c94ea42e772ef4d29fe92973636b6813d4eaffc/paper-1.21.4-232.jar`;
|
|
19
|
-
const CACHEJAR = resolve(CACHE, JAR);
|
|
20
15
|
const WS_PORT = 17368;
|
|
21
16
|
|
|
22
17
|
const YES = process.argv.includes('-y') || process.env.CI === 'true';
|
|
@@ -26,6 +21,50 @@ const STOP = (() => { const a = process.argv.find(a => a.startsWith('--stop='));
|
|
|
26
21
|
const cfg = JSON.parse(readFileSync(resolve(ROOT, 'yeow.config.json'), 'utf-8'));
|
|
27
22
|
const RUNTIME = resolve(ROOT, '.yeow', 'assets', 'yeow-runtime-0.1.0.jar');
|
|
28
23
|
|
|
24
|
+
// Dev server config (optional, from yeow.config.json)
|
|
25
|
+
const devCfg = cfg.dev || {};
|
|
26
|
+
const PAPER_VERSION = devCfg.paperVersion || '1.21.4';
|
|
27
|
+
const PAPER_URL = devCfg.paperJar || null;
|
|
28
|
+
let PAPER_PATH = null;
|
|
29
|
+
let PAPER_JAR = null;
|
|
30
|
+
|
|
31
|
+
if (PAPER_URL) {
|
|
32
|
+
if (PAPER_URL.startsWith('http://') || PAPER_URL.startsWith('https://')) {
|
|
33
|
+
PAPER_PATH = resolve(CACHE, basename(new URL(PAPER_URL).pathname));
|
|
34
|
+
PAPER_JAR = basename(new URL(PAPER_URL).pathname);
|
|
35
|
+
} else {
|
|
36
|
+
PAPER_PATH = PAPER_URL;
|
|
37
|
+
PAPER_JAR = basename(PAPER_URL);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
PAPER_JAR = `paper-${PAPER_VERSION}.jar`;
|
|
41
|
+
PAPER_PATH = resolve(CACHE, PAPER_JAR);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Config hash — detect changes and recreate dev server
|
|
45
|
+
const CONFIG_HASH_FILE = resolve(DEVDIR, '.config-hash');
|
|
46
|
+
function configHash() { return JSON.stringify({ paperUrl: PAPER_URL, paperVersion: PAPER_VERSION }); }
|
|
47
|
+
|
|
48
|
+
function checkConfigChanged() {
|
|
49
|
+
if (!existsSync(CONFIG_HASH_FILE)) return true;
|
|
50
|
+
try {
|
|
51
|
+
return readFileSync(CONFIG_HASH_FILE, 'utf-8').trim() !== configHash();
|
|
52
|
+
} catch { return true; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function saveConfigHash() {
|
|
56
|
+
mkdirSync(DEVDIR, { recursive: true });
|
|
57
|
+
writeFileSync(CONFIG_HASH_FILE, configHash());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (checkConfigChanged()) {
|
|
61
|
+
if (existsSync(SERVER)) {
|
|
62
|
+
console.log(' Paper config changed — recreating dev server...');
|
|
63
|
+
rmSync(SERVER, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
saveConfigHash();
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
const c = { r: '\x1b[0m', b: '\x1b[1m', d: '\x1b[2m', g: '\x1b[32m', y: '\x1b[33m', B: '\x1b[34m', C: '\x1b[36m', R: '\x1b[31m', ok: '\x1b[32m✓\x1b[0m', fail: '\x1b[31m✗\x1b[0m', info: '\x1b[36mⓘ\x1b[0m', warn: '\x1b[33m⚠\x1b[0m' };
|
|
30
69
|
const log = (msg, color = '') => console.log(`${c.d}[${new Date().toLocaleTimeString()}]${c.r} ${color}${msg}${c.r}`);
|
|
31
70
|
const ok = msg => log(`${c.ok} ${msg}`, c.g);
|
|
@@ -36,6 +75,29 @@ const warn = msg => log(`${c.warn} ${msg}`, c.y);
|
|
|
36
75
|
let proc = null;
|
|
37
76
|
let wss = null;
|
|
38
77
|
|
|
78
|
+
// ── Graceful shutdown ────────────────────────────────────────────
|
|
79
|
+
function cleanup() {
|
|
80
|
+
if (proc && !proc.killed) {
|
|
81
|
+
try { proc.stdin.write('stop\n'); } catch {}
|
|
82
|
+
setTimeout(() => { if (proc && !proc.killed) try { proc.kill(); } catch {} }, 5000);
|
|
83
|
+
}
|
|
84
|
+
if (wss) { try { wss.close(); } catch {} }
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
process.on('SIGINT', cleanup);
|
|
88
|
+
process.on('SIGTERM', cleanup);
|
|
89
|
+
process.on('exit', () => { if (proc && !proc.killed) try { proc.kill(); } catch {} });
|
|
90
|
+
// Windows Ctrl+C in terminal
|
|
91
|
+
if (process.platform === 'win32') {
|
|
92
|
+
import('readline').then(rl => {
|
|
93
|
+
rl.emitKeypressEvents(process.stdin);
|
|
94
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
95
|
+
process.stdin.on('keypress', (str, key) => {
|
|
96
|
+
if ((key.ctrl && key.name === 'c') || key.name === 'escape') cleanup();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
39
101
|
// ── WebSocket Server ────────────────────────────────────────────
|
|
40
102
|
function startWebSocket() {
|
|
41
103
|
const server = createServer();
|
|
@@ -83,13 +145,38 @@ function download(url, dest, agent) {
|
|
|
83
145
|
}
|
|
84
146
|
|
|
85
147
|
async function ensurePaper() {
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
148
|
+
if (PAPER_URL) {
|
|
149
|
+
// User-specified URL or file path
|
|
150
|
+
if (PAPER_URL.startsWith('http://') || PAPER_URL.startsWith('https://')) {
|
|
151
|
+
if (existsSync(PAPER_PATH) && statSync(PAPER_PATH).size > 1_000_000) { ok('Paper found in cache'); return; }
|
|
152
|
+
mkdirSync(CACHE, { recursive: true });
|
|
153
|
+
let agent = null;
|
|
154
|
+
if (PROXY) { info(`Proxy: ${PROXY}`); try { const { HttpsProxyAgent } = await import('https-proxy-agent'); agent = new HttpsProxyAgent(PROXY); } catch {} }
|
|
155
|
+
info('Downloading Paper...');
|
|
156
|
+
try { await download(PAPER_URL, PAPER_PATH, agent); ok('Paper downloaded'); }
|
|
157
|
+
catch (e) { fail('Download failed: ' + e.message); console.log(' Manually: ' + PAPER_PATH); process.exit(1); }
|
|
158
|
+
} else {
|
|
159
|
+
if (!existsSync(PAPER_PATH)) { fail(`Paper JAR not found: ${PAPER_PATH}`); process.exit(1); }
|
|
160
|
+
ok('Paper found at specified path');
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
if (existsSync(PAPER_PATH) && statSync(PAPER_PATH).size > 1_000_000) { ok('Paper found in cache'); return; }
|
|
164
|
+
mkdirSync(CACHE, { recursive: true });
|
|
165
|
+
info('Downloading Paper...');
|
|
166
|
+
try {
|
|
167
|
+
const apiUrl = `https://api.papermc.io/v2/projects/paper/versions/${PAPER_VERSION}`;
|
|
168
|
+
const versions = await fetch(apiUrl).then(r => r.json());
|
|
169
|
+
const latestBuild = versions.builds[versions.builds.length - 1];
|
|
170
|
+
const dlUrl = `https://api.papermc.io/v2/projects/paper/versions/${PAPER_VERSION}/builds/${latestBuild}/downloads/paper-${PAPER_VERSION}-${latestBuild}.jar`;
|
|
171
|
+
await download(dlUrl, PAPER_PATH, null);
|
|
172
|
+
ok('Paper downloaded');
|
|
173
|
+
} catch (e) {
|
|
174
|
+
fail('Download failed: ' + e.message);
|
|
175
|
+
console.log(' Manually download to: ' + PAPER_PATH);
|
|
176
|
+
console.log(' Or set "paperJar" in yeow.config.json dev section');
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
93
180
|
}
|
|
94
181
|
|
|
95
182
|
function buildPlugin() {
|
|
@@ -104,8 +191,8 @@ function copyToPlugins(src, label) {
|
|
|
104
191
|
}
|
|
105
192
|
|
|
106
193
|
async function initServer() {
|
|
107
|
-
const jar = resolve(SERVER,
|
|
108
|
-
if (!existsSync(jar)) copyFileSync(
|
|
194
|
+
const jar = resolve(SERVER, PAPER_JAR);
|
|
195
|
+
if (!existsSync(jar)) copyFileSync(PAPER_PATH, jar);
|
|
109
196
|
const eula = resolve(SERVER, 'eula.txt');
|
|
110
197
|
if (existsSync(eula) && readFileSync(eula, 'utf-8').includes('eula=true')) return;
|
|
111
198
|
|
|
@@ -157,7 +244,7 @@ function startHotReload() {
|
|
|
157
244
|
codeFile: compiled.replace(/\\/g, '/'),
|
|
158
245
|
assetsDir: existsSync(assetsDir) ? assetsDir.replace(/\\/g, '/') : null,
|
|
159
246
|
});
|
|
160
|
-
_consumer = null;
|
|
247
|
+
_consumer = null;
|
|
161
248
|
ok('Hot reload sent via WebSocket');
|
|
162
249
|
}
|
|
163
250
|
} catch (e) {
|
|
@@ -168,13 +255,11 @@ function startHotReload() {
|
|
|
168
255
|
}, 300);
|
|
169
256
|
};
|
|
170
257
|
|
|
171
|
-
// Watch src/ for changes
|
|
172
258
|
watch(srcDir, { recursive: true }, (event, file) => {
|
|
173
259
|
if (!file || !file.endsWith('.' + ext)) return;
|
|
174
260
|
rebuildAndNotify();
|
|
175
261
|
});
|
|
176
262
|
|
|
177
|
-
// Watch assets/ for changes
|
|
178
263
|
if (existsSync(assetsDir)) {
|
|
179
264
|
watch(assetsDir, { recursive: true }, (event, file) => {
|
|
180
265
|
rebuildAndNotify();
|
|
@@ -203,7 +288,6 @@ async function printFormattedError(err) {
|
|
|
203
288
|
if (err.context) out += ` ${c.D}context: ${err.context}${c.r}\n`;
|
|
204
289
|
out += ` ${c.Y}${err.message}${c.r}\n`;
|
|
205
290
|
|
|
206
|
-
// Check if stack contains any main.js frames — if so, load source-map consumer
|
|
207
291
|
const hasMainJs = err.stack?.match(/main\.js:\d+:\d+/) || err.fileName === 'main.js';
|
|
208
292
|
let consumer = null;
|
|
209
293
|
if (hasMainJs) {
|
|
@@ -214,7 +298,6 @@ async function printFormattedError(err) {
|
|
|
214
298
|
out += ` ${c.D}(source-map not found: ${existsSync(mapFile) ? 'exists but failed to parse' : 'missing at ' + mapFile})${c.r}\n`;
|
|
215
299
|
}
|
|
216
300
|
|
|
217
|
-
// Resolve all stack frames
|
|
218
301
|
const frames = [];
|
|
219
302
|
if (err.stack) {
|
|
220
303
|
for (const rawLine of err.stack.split('\n')) {
|
|
@@ -222,7 +305,6 @@ async function printFormattedError(err) {
|
|
|
222
305
|
if (m && consumer) {
|
|
223
306
|
const orig = consumer.originalPositionFor({ line: parseInt(m[1]), column: parseInt(m[2]) });
|
|
224
307
|
if (!orig?.source) {
|
|
225
|
-
// Try adjusted column
|
|
226
308
|
const orig2 = consumer.originalPositionFor({ line: parseInt(m[1]), column: parseInt(m[2]) - 1 });
|
|
227
309
|
if (orig2?.source) { orig.source = orig2.source; orig.line = orig2.line; orig.column = orig2.column; }
|
|
228
310
|
}
|
|
@@ -233,13 +315,10 @@ async function printFormattedError(err) {
|
|
|
233
315
|
}
|
|
234
316
|
}
|
|
235
317
|
|
|
236
|
-
// Find first frame from user's src/ directory (not node_modules)
|
|
237
318
|
let ctxFrame = frames.find(f => f.orig?.source?.match(/[\\/]src[\\/]/) && !f.orig.source.includes('node_modules'))?.orig || null;
|
|
238
319
|
|
|
239
|
-
// Show context for the chosen frame
|
|
240
320
|
if (ctxFrame?.source && ctxFrame.line && consumer) {
|
|
241
321
|
const srcPath = ctxFrame.source.replace(/^\.\.\/\.\.\//, '');
|
|
242
|
-
// Preserve function name
|
|
243
322
|
const ctxRaw = frames.find(f => f.orig === ctxFrame)?.raw || '';
|
|
244
323
|
const fnM = ctxRaw.match(/at\s+(\S+)\s+\(/);
|
|
245
324
|
const fnS = fnM ? fnM[1] + ' ' : '';
|
|
@@ -262,7 +341,6 @@ async function printFormattedError(err) {
|
|
|
262
341
|
}
|
|
263
342
|
}
|
|
264
343
|
|
|
265
|
-
// Print resolved stack (all frames)
|
|
266
344
|
if (frames.length > 0) {
|
|
267
345
|
out += ` ${c.D}Stack:${c.r}\n`;
|
|
268
346
|
for (const f of frames) {
|
|
@@ -284,11 +362,10 @@ async function printFormattedError(err) {
|
|
|
284
362
|
|
|
285
363
|
function startServer() {
|
|
286
364
|
const jvmArgs = ['-Xmx4G', '-Xms4G', '-Dyeow.dev=true', '-Dyeow.ws.port=' + WS_PORT];
|
|
287
|
-
info(`\nStarting Paper ${
|
|
288
|
-
proc = spawn('java', [...jvmArgs, '-jar', resolve(SERVER,
|
|
365
|
+
info(`\nStarting Paper ${PAPER_VERSION} server...`);
|
|
366
|
+
proc = spawn('java', [...jvmArgs, '-jar', resolve(SERVER, PAPER_JAR), '--nogui'], { cwd: SERVER, stdio: ['pipe', 'inherit', 'inherit'] });
|
|
289
367
|
proc.on('exit', code => { warn(`Server exited (${code})`); if (wss) wss.close(); process.exit(0); });
|
|
290
|
-
|
|
291
|
-
if (STOP) { info(`Auto-stop in ${STOP}s`); setTimeout(() => { warn('Auto-stop'); if (proc && !proc.killed) proc.stdin.write('stop\n'); setTimeout(() => { if (proc && !proc.killed) proc.kill(); process.exit(0); }, 10000); }, STOP * 1000); }
|
|
368
|
+
if (STOP) { info(`Auto-stop in ${STOP}s`); setTimeout(() => { warn('Auto-stop'); cleanup(); }, STOP * 1000); }
|
|
292
369
|
}
|
|
293
370
|
|
|
294
371
|
async function main() {
|
|
@@ -308,5 +385,4 @@ async function main() {
|
|
|
308
385
|
startServer();
|
|
309
386
|
}
|
|
310
387
|
|
|
311
|
-
process.on('SIGINT', () => { if (proc) { proc.stdin.write('stop\n'); setTimeout(() => { if (proc && !proc.killed) proc.kill(); if (wss) wss.close(); process.exit(0); }, 5000); } });
|
|
312
388
|
main().catch(e => { fail(e.message); process.exit(1); });
|
|
@@ -1,27 +1,55 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
onInit, onLoad, onUnload, registerCommand, eventOn,
|
|
3
|
+
Player, Location, pdcSet, pdcGet, log,
|
|
4
|
+
} from 'yeow-api';
|
|
2
5
|
|
|
3
|
-
onInit(() => {
|
|
4
|
-
onLoad(() => {
|
|
6
|
+
onInit(() => { log.info('Init'); });
|
|
7
|
+
onLoad(() => { log.info('Ready'); });
|
|
5
8
|
|
|
6
9
|
onLoad(() => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
// ── /back: 返回死亡位置 ──
|
|
11
|
+
eventOn('playerDeath', async (e) => {
|
|
12
|
+
const loc = e.player.location;
|
|
13
|
+
if (!loc) return;
|
|
14
|
+
const data = JSON.stringify({ x: loc.x, y: loc.y, z: loc.z, world: loc.world || e.player.world });
|
|
15
|
+
pdcSet(e.player.uuid, 'back.deathLocation', data);
|
|
16
|
+
await e.player.sendMessage(
|
|
17
|
+
'<red>You died!</red> <gray>Use</gray> <click:run_command:/back><aqua><u>/back</u></aqua></click> <gray>to return</gray>',
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
registerCommand('back', {
|
|
22
|
+
description: 'Teleport to your death location',
|
|
23
|
+
executor: async (p) => {
|
|
24
|
+
const raw = await pdcGet(p.sender.uuid, 'back.deathLocation');
|
|
25
|
+
if (!raw) return p.sender.sendMessage('<red>No death location recorded</red>');
|
|
26
|
+
|
|
27
|
+
const loc = JSON.parse(raw);
|
|
28
|
+
const player = await Player.get(p.sender.uuid);
|
|
29
|
+
if (!player) return;
|
|
30
|
+
await player.teleport(new Location(loc.x, loc.y, loc.z, 0, 0, loc.world));
|
|
31
|
+
p.sender.sendMessage('<green>Teleported to death location</green>');
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ── /ping ──
|
|
36
|
+
registerCommand('ping', {
|
|
37
|
+
executor: async (p) => {
|
|
38
|
+
const player = await Player.get(p.sender.uuid);
|
|
39
|
+
if (player) p.sender.sendMessage(`Ping: ${player.ping}ms`);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// ── 事件 ──
|
|
44
|
+
eventOn('playerJoin', (e) => {
|
|
45
|
+
log.info(`${e.player.name} joined`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
eventOn('blockBreak', (e) => {
|
|
49
|
+
if (e.block === 'minecraft:bedrock') e.cancelled = true;
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
onUnload(() => {
|
|
54
|
+
log.info('Unloaded');
|
|
27
55
|
});
|
|
@@ -1,28 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
onInit, onLoad, onUnload, registerCommand, eventOn,
|
|
3
|
+
Player, Location, pdcSet, pdcGet, log,
|
|
4
|
+
} from 'yeow-api';
|
|
5
|
+
import type { PlayerDeathEvent } from 'yeow-api';
|
|
3
6
|
|
|
4
|
-
onInit(() => {
|
|
5
|
-
onLoad(() => {
|
|
7
|
+
onInit(() => { log.info('Init'); });
|
|
8
|
+
onLoad(() => { log.info('Ready'); });
|
|
6
9
|
|
|
7
10
|
onLoad(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
// ── /back: 返回死亡位置 ──
|
|
12
|
+
eventOn('playerDeath', async (e: PlayerDeathEvent) => {
|
|
13
|
+
const loc = e.player.location;
|
|
14
|
+
if (!loc) return;
|
|
15
|
+
const data = JSON.stringify({ x: loc.x, y: loc.y, z: loc.z, world: loc.world || e.player.world });
|
|
16
|
+
pdcSet(e.player.uuid, 'back.deathLocation', data);
|
|
17
|
+
await e.player.sendMessage(
|
|
18
|
+
'<red>You died!</red> <gray>Use</gray> <click:run_command:/back><aqua><u>/back</u></aqua></click> <gray>to return</gray>',
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
registerCommand('back', {
|
|
23
|
+
description: 'Teleport to your death location',
|
|
24
|
+
executor: async (p) => {
|
|
25
|
+
const raw = await pdcGet(p.sender.uuid, 'back.deathLocation');
|
|
26
|
+
if (!raw) return p.sender.sendMessage('<red>No death location recorded</red>');
|
|
27
|
+
|
|
28
|
+
const loc = JSON.parse(raw);
|
|
29
|
+
const player = await Player.get(p.sender.uuid);
|
|
30
|
+
if (!player) return;
|
|
31
|
+
await player.teleport(new Location(loc.x, loc.y, loc.z, 0, 0, loc.world));
|
|
32
|
+
p.sender.sendMessage('<green>Teleported to death location</green>');
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ── /ping ──
|
|
37
|
+
registerCommand('ping', {
|
|
38
|
+
executor: async (p) => {
|
|
39
|
+
const player = await Player.get(p.sender.uuid);
|
|
40
|
+
if (player) p.sender.sendMessage(`Ping: ${player.ping}ms`);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── 事件 ──
|
|
45
|
+
eventOn('playerJoin', (e) => {
|
|
46
|
+
log.info(`${e.player.name} joined`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
eventOn('blockBreak', (e) => {
|
|
50
|
+
if (e.block === 'minecraft:bedrock') e.cancelled = true;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
onUnload(() => {
|
|
55
|
+
log.info('Unloaded');
|
|
28
56
|
});
|
|
Binary file
|