Skip to content

Commit 787c8f5

Browse files
committed
update
1 parent 5237143 commit 787c8f5

File tree

3 files changed

+201
-19
lines changed

3 files changed

+201
-19
lines changed

NbsPlayer/NbsPlayer.lls.js

Lines changed: 157 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global ll mc system Format PermType logger */
1+
/* global ll mc system Format PermType ParamType logger BinaryStream */
22
// LiteLoaderScript Dev Helper
33
/// <reference path="E:\Coding\bds\.vscode\LLSEDevHelper/Library/JS/Api.js" />
44

@@ -59,7 +59,7 @@ const playTasks = new Map();
5959
function convertNbs(name, callback) {
6060
const nbsPath = `${pluginDataPath}${name}`;
6161
const nbsCachePath = `${pluginCachePath}${name}.json`;
62-
return system.newProcess(
62+
const ret = system.newProcess(
6363
`${nbsConvertorPath} -f "${nbsPath}" -o "${nbsCachePath}"`,
6464
(code, returns) => {
6565
if (!code === 0) {
@@ -79,16 +79,20 @@ function convertNbs(name, callback) {
7979
let j;
8080
try {
8181
j = JSON.parse(t);
82-
} catch {
82+
} catch (e) {
83+
logger.error(`转换nbs文件出错\n${e.stack}`);
8384
callback(false, '解析转换后Json失败');
8485
return;
8586
}
8687

8788
File.delete(nbsCachePath);
8889
callback(true, j);
89-
});
90-
}
90+
})
91+
.catch((e) => logger.error(`未知错误\n${e.stack}`));
92+
},
93+
10 * 1000
9194
);
95+
if (!ret) callback(false, '运行NbsConvertor失败');
9296
}
9397

9498
/**
@@ -124,12 +128,36 @@ function formatMsTime(msTime) {
124128
return `${min}:${sec}.${ms}`;
125129
}
126130

131+
/**
132+
* @param {BinaryStream} bs
133+
* @param {String} sound
134+
* @param {FloatPos} position
135+
* @param {Number} volume
136+
* @param {Number} pitch
137+
* @returns
138+
*/
139+
function getPlaySoundDataPack(bs, sound, position, volume, pitch) {
140+
bs.reset();
141+
142+
bs.writeString(sound);
143+
144+
// bs.writeVec3(position);
145+
bs.writeVarInt(position.x * 8);
146+
bs.writeUnsignedVarInt(position.y * 8);
147+
bs.writeVarInt(position.z * 8);
148+
149+
bs.writeFloat(volume);
150+
bs.writeFloat(pitch);
151+
152+
return bs.createPacket(86);
153+
}
154+
127155
/**
128156
* @param {Player} player
129157
* @param {String} nbsName
130158
*/
131159
function startPlay(player, nbsName) {
132-
const { xuid, realName } = player;
160+
const { xuid } = player;
133161
const playingTask = playTasks.get(xuid);
134162
if (playingTask) stopPlay(xuid);
135163

@@ -186,6 +214,9 @@ function startPlay(player, nbsName) {
186214
note: v,
187215
}));
188216
const totalNotes = noteAndTime.length;
217+
218+
const bs = new BinaryStream();
219+
189220
const startTime = Date.now();
190221

191222
const task = () => {
@@ -223,12 +254,24 @@ function startPlay(player, nbsName) {
223254
const layerVol = layerVolMap.get(layer) || 100;
224255
const finalVol = (velocity * (layerVol / 100)) / 100;
225256

226-
const cmd =
227-
`execute "${realName}" ~~~ ` +
228-
`playsound ${instrumentMapEx.get(instrument)} @s ~ ~1.65 ~ ` +
229-
`${finalVol} ${keyMap.get(finalPitch)}`;
257+
const { pos } = pl;
258+
pos.y += 0.37;
259+
260+
pl.sendPacket(
261+
getPlaySoundDataPack(
262+
bs,
263+
instrumentMapEx.get(instrument) || '',
264+
pos,
265+
finalVol || 0,
266+
keyMap.get(finalPitch) || 1
267+
)
268+
);
269+
// const cmd =
270+
// `execute "${realName}" ~~~ ` +
271+
// `playsound ${instrumentMapEx.get(instrument)} @s ~ ~1.65 ~ ` +
272+
// `${finalVol} ${keyMap.get(finalPitch)}`;
230273
// log(cmd);
231-
mc.runcmdEx(cmd);
274+
// mc.runcmdEx(cmd);
232275
});
233276

234277
const timeSpentStr = formatMsTime(timeSpent);
@@ -256,6 +299,17 @@ function nbsForm(player) {
256299
if (v.toLowerCase().endsWith('.nbs')) musics.push(v);
257300
});
258301

302+
if (musics.length === 0) {
303+
player.sendModalForm(
304+
`${Aqua}${pluginName}`,
305+
`${Green}插件数据目录内还没有歌曲文件哦!赶快去寻找nbs音乐来播放吧!`,
306+
`知道了`,
307+
`知道了`,
308+
() => {}
309+
);
310+
return;
311+
}
312+
259313
const search = (param) => {
260314
const paramL = param.toLowerCase().replace(' ', '');
261315
const result = [];
@@ -328,6 +382,17 @@ function nbsForm(player) {
328382
}
329383

330384
if (i === 1) {
385+
if (maxPage < 2) {
386+
player.sendModalForm(
387+
`${Aqua}${pluginName}`,
388+
`${Red}页面总数小于2,无法跳转`,
389+
`知道了`,
390+
`知道了`,
391+
() => sendForm(page)
392+
);
393+
return;
394+
}
395+
331396
const toPageForm = mc
332397
.newCustomForm()
333398
.setTitle(`${Aqua}${pluginName}`)
@@ -364,21 +429,79 @@ function nbsForm(player) {
364429
sendForm(1);
365430
}
366431

432+
/**
433+
* 去两侧引号
434+
* @param {String} str
435+
*/
436+
function trimQuote(str) {
437+
if (str && str.startsWith('"') && str.endsWith('"'))
438+
return str.slice(1, str.length - 1);
439+
return str;
440+
}
441+
367442
(() => {
368443
const cmd = mc.newCommand('nbsplayer', '来首音乐嘛?', PermType.Any);
369444
cmd.setAlias('nbs');
445+
cmd.optional('filename', ParamType.RawText);
446+
cmd.overload(['filename']);
370447

371-
cmd.setCallback((_, origin, out) => {
448+
cmd.setCallback((_, origin, out, result) => {
372449
const { player } = origin;
373450
if (!player) {
374451
out.error('该命令只能由玩家执行');
375452
return false;
376453
}
454+
455+
const { filename } = result;
456+
if (filename) {
457+
const filePath = `${pluginDataPath}${trimQuote(filename)}`;
458+
if (!File.exists(filePath)) {
459+
out.error('文件不存在!');
460+
return false;
461+
}
462+
463+
startPlay(player, trimQuote(filename));
464+
return true;
465+
}
466+
377467
nbsForm(player);
378468
return true;
379469
});
380470

381-
cmd.overload();
471+
cmd.setup();
472+
})();
473+
474+
(() => {
475+
const cmd = mc.newCommand('nbsplay', '管理员播放指令');
476+
cmd.mandatory('player', ParamType.Player);
477+
cmd.mandatory('filename', ParamType.RawText);
478+
cmd.optional('forcePlay', ParamType.Bool);
479+
cmd.overload(['player', 'filename', 'forcePlay']);
480+
481+
cmd.setCallback((_, __, out, result) => {
482+
const { player, filename, forcePlay } = result;
483+
const filePath = `${pluginDataPath}${trimQuote(filename)}`;
484+
if (player.length === 0) {
485+
out.error('玩家不在线');
486+
return false;
487+
}
488+
489+
if (!File.exists(filePath)) {
490+
out.error('文件不存在!');
491+
return false;
492+
}
493+
494+
player.forEach((p) => {
495+
if (forcePlay || !playTasks.get(p.xuid)) {
496+
startPlay(p, filename);
497+
out.success(`成功为 ${p.name} 播放 ${filename}`);
498+
return;
499+
}
500+
out.error(`玩家 ${p.name} 正在播放中,操作失败`);
501+
});
502+
return true;
503+
});
504+
382505
cmd.setup();
383506
})();
384507

@@ -402,9 +525,29 @@ function nbsForm(player) {
402525
cmd.setup();
403526
})();
404527

528+
(() => {
529+
const cmd = mc.newCommand('nbsisplaying', '玩家是否正在播放', PermType.Any);
530+
531+
cmd.setCallback((_, origin, out) => {
532+
const { player } = origin;
533+
if (!player) {
534+
out.error('该命令只能由玩家执行');
535+
return false;
536+
}
537+
538+
if (playTasks.get(player.xuid)) return out.success('true');
539+
540+
out.error('false');
541+
return false;
542+
});
543+
544+
cmd.overload();
545+
cmd.setup();
546+
})();
547+
405548
mc.listen('onLeft', (pl) => stopPlay(pl.xuid));
406549

407-
ll.registerPlugin(pluginName, '在服务器播放NBS音乐!', [0, 1, 1], {
550+
ll.registerPlugin(pluginName, '在服务器播放NBS音乐!', [0, 2, 0], {
408551
Author: 'student_2333',
409552
License: 'Apache-2.0',
410553
});

NbsPlayer/readme.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ BDS 中的 NBS 音乐播放器!
1010
### 运作原理
1111

1212
1. 插件通过[NbsConvertor](NbsConvertor)将 nbs 文件转换成 json 格式存储在 `cache` 目录中
13-
2. 读取 json 数据,通过`setInterval`设置循环执行函数来对时并且根据已播放时间根据 json 内的各数据生成`playsound`命令并执行
13+
2. 读取 json 数据,通过`setInterval`设置循环执行函数获取当前时间来对时和播放,并且根据已播放时间根据 json 解析后的各数据生成`PlaySoundPacket`数据包发送给客户端
1414

1515
### 特点
1616

@@ -21,7 +21,8 @@ BDS 中的 NBS 音乐播放器!
2121

2222
### 缺点
2323

24-
- 播放效果不好,会有小卡顿(对不上拍)(脚本语言 / 调用 BDS 执行命令效率问题?)
24+
- 播放效果不好,会有小卡顿(对不上拍)(脚本语言~~ / 调用 BDS 执行命令~~效率问题?)
25+
- Tip:`0.2.0`版本已换为向客户端发送数据包,卡顿问题有所改善
2526
- 无法实现左右声道偏离(`playsound`局限)
2627
- 稍微动一下视角声道就会偏(`playsound`局限)
2728
- 插件没有经过深度测试,可能会有小 bug
@@ -31,22 +32,54 @@ BDS 中的 NBS 音乐播放器!
3132
![1](readme/1.png)
3233
![2](readme/2.png)
3334
![3](readme/3.png)
34-
![4](readme/4.png)
35-
![5](readme/5.png)
35+
![4](readme/4.png)
3636

3737
### 演示
3838

3939
<https://www.bilibili.com/video/bv1gT41177sm>
4040

4141
<iframe src="//player.bilibili.com/player.html?aid=471512773&bvid=BV1gT41177sm&cid=789620051&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
4242

43+
### 指令
44+
45+
#### `nbsplayer` - 打开插件菜单 / 播放指定音乐
46+
47+
- 格式:`nbsplayer [filename: text]`
48+
- 参数:
49+
- `filename` - *(可选)*欲播放的文件名
50+
- 权限:所有人
51+
- 限制:只能由玩家以及`execute`命令执行
52+
53+
#### `nbsplay` - OP 播放指令
54+
55+
- 格式:`nbsplay <player: target> <filename: text> [forcePlay: Boolean]`
56+
- 参数:
57+
- `player` - 欲执行操作的玩家
58+
- `filename` - 欲播放的文件名
59+
- `forcePlay` - *(可选)*是否强制让正在播放音乐的玩家播放此音乐
60+
- 权限:管理员
61+
62+
#### `nbstop` - 停止播放
63+
64+
- 格式:`nbstop`
65+
- 参数:无
66+
- 权限:所有人
67+
- 限制:只能由玩家以及`execute`命令执行
68+
69+
#### `nbsisplaying` - 查询玩家播放状态
70+
71+
- 格式:`nbsisplaying`
72+
- 参数:无
73+
- 权限:所有人
74+
- 限制:只能由玩家以及`execute`命令执行
75+
4376
## 安装
4477

4578
请去 [Releases](https://github.com/lgc2333/LLSEPlugins/releases) 下载最新版插件附件,解压到 bds 的插件目录
4679

4780
## 配置
4881

49-
直接将 nbs 文件放置在插件数据目录即可~~,注意文件名不要有特殊字符、中文等,否则可能会出错~~(应该已修复)
82+
直接将 nbs 文件放置在插件数据目录即可,无需重启服务器~~,注意文件名不要有特殊字符、中文等,否则可能会出错~~(应该已修复)
5083
例图:
5184
![6](readme/6.png)
5285

@@ -78,3 +111,9 @@ QQ:3076823485
78111
- 修复 nbs 文件名不能为特殊符号或中文的问题
79112
- 修复无法播放最后一 tick 的音符的 bug
80113
- 加入音符数量显示
114+
- 0.2.0
115+
- 换用向客户端发送数据包的方式播放音效
116+
- NbsConvertor 的运行超时限制为 10s
117+
- 加入歌曲列表为 0 时的提示,与页数小于 2 无法跳页的提示
118+
- `nbsplayer`命令加入可选参数`filename`,可以直接指定播放的文件名
119+
- 加入命令`nbsplay` `nbsisplaying`[指令列表](#指令)

NbsPlayer/readme/5.png

-23.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)