dou-cl 1.0.1 → 1.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/PUBLISH.md +3 -3
- package/README.md +35 -12
- package/bin/dou-cl.js +50 -30
- package/package.json +5 -3
- package/src/app.js +30 -43
- package/src/net/client.js +268 -130
- package/src/net/server.js +278 -309
- package/src/ui.js +3 -3
package/PUBLISH.md
CHANGED
|
@@ -238,9 +238,9 @@ npx dou-cl
|
|
|
238
238
|
npx dou-cl -g doudizhu # 跳过菜单,直接开一局(最隐蔽)
|
|
239
239
|
npx dou-cl -g fivek # 510K
|
|
240
240
|
npx dou-cl --skin hex # 换个伪装皮肤
|
|
241
|
-
npx dou-cl --serve --port 8080
|
|
242
|
-
npx dou-cl --join 192.168.1.10:8080
|
|
243
|
-
npx dou-cl --
|
|
241
|
+
npx dou-cl --serve --port 8080 -g doudizhu # 本机开一个房间(服务器不占座位)
|
|
242
|
+
npx dou-cl --join 192.168.1.10:8080 # 连进这个房间(地址本身就是房间)
|
|
243
|
+
npx dou-cl --serve --ports 10000-12000 # 一次开一片房间,每个端口一个
|
|
244
244
|
```
|
|
245
245
|
|
|
246
246
|
### ⚠️ 刚发完那几分钟,npx 会拉不到
|
package/README.md
CHANGED
|
@@ -11,16 +11,47 @@
|
|
|
11
11
|
<p align="center"><b>一个伪装成开发控制台的命令行牌桌。</b></p>
|
|
12
12
|
|
|
13
13
|
跑起来就是一屏正常的开发输出:服务端日志、`xxd` 转储、JSON 帧、git diff。
|
|
14
|
-
|
|
14
|
+
里面其实是**斗地主 / 跑得快 / 510K**,牌局信息一条不删,只是换了层壳。
|
|
15
15
|
工位上扫一眼屏幕的人,只会觉得你在看构建日志。
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npx dou-cl
|
|
19
|
-
npx dou-cl
|
|
18
|
+
npx dou-cl --join 43.159.48.211:10001 # 进公共房间,和真人打
|
|
19
|
+
npx dou-cl # 单机,电脑陪打
|
|
20
|
+
npx dou-cl -g doudizhu # 跳过菜单直接开局(最隐蔽)
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
25
|
+
## 公共房间
|
|
26
|
+
|
|
27
|
+
有人开了一台公共服务器:**`43.159.48.211`,端口 `10000`–`12000`,一共 2001 个房间。**
|
|
28
|
+
|
|
29
|
+
每个端口是一个独立房间,**随便挑一个进去就行** —— 或者可以和朋友约定一个端口。
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx dou-cl --join 43.159.48.211:10001
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- 人不够会一直等着,**人齐了自动开局**;等的时候敲 `s` 让电脑先补上,`q` 退出
|
|
36
|
+
- 人满了会直接告诉你(`room full`),**换个端口**再试即可
|
|
37
|
+
- 只想自己练手就 `npx dou-cl`,单机三位电脑陪打
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 局域网联机
|
|
42
|
+
|
|
43
|
+
**一个 `ip:port` 就是一个房间**,服务器自己不占座位,只是托管。
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx dou-cl --serve --port 8080 -g doudizhu # 本机开一个房间
|
|
47
|
+
npx dou-cl --join 192.168.1.10:8080 # 同事连进来,地址就是房间号
|
|
48
|
+
npx dou-cl --serve --ports 10000-12000 # 一次开一片,每个端口一个独立房间
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
换皮肤、`reveal` 这类纯本地的操作**任何时刻都能用**,不用等你的回合。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
24
55
|
## 四种伪装
|
|
25
56
|
|
|
26
57
|
**`log`(默认)** —— 服务端日志流。看 `hand[...]` / `peers=[...]` / `table=`。
|
|
@@ -75,7 +106,7 @@ npx dou-cl --skins # 列出全部
|
|
|
75
106
|
| 操作 | 写法 |
|
|
76
107
|
| --- | --- |
|
|
77
108
|
| 出牌 | 直接敲点数:`34567` 或 `3 4 5 6 7`;指定花色 `5s 5h` |
|
|
78
|
-
| 不要 / 提示 / 帮助 / 退出 | `p` / `h` / `?` / `
|
|
109
|
+
| 不要 / 提示 / 帮助 / 退出 | `p` / `h` / `?` / `q` |
|
|
79
110
|
| 看真身 / 换皮肤 / 重画 | `reveal` / `skin <名>` / `redraw` |
|
|
80
111
|
|
|
81
112
|
同花顺、五十K **不用管花色**,直接敲 `56789` / `50K`,程序自动挑同花色那组。
|
|
@@ -90,14 +121,6 @@ npx dou-cl --skins # 列出全部
|
|
|
90
121
|
| **跑得快** | 3 | 一副牌去大小王,持 ♠3 者先出且首手必带 ♠3 |
|
|
91
122
|
| **510K** | 4 | 两副牌,5/10/K 计分;五十K 是通吃牌型 |
|
|
92
123
|
|
|
93
|
-
都可以联网:一个人 `--serve` 开服务器,其他人 `--join <地址> --code <房间号>` 进来,
|
|
94
|
-
或 `--match <玩法>` 快速匹配,人数不够由电脑补齐。
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
npx dou-cl --serve --port 8080
|
|
98
|
-
npx dou-cl --join 192.168.1.10:8080 --code ABCD
|
|
99
|
-
```
|
|
100
|
-
|
|
101
124
|
---
|
|
102
125
|
|
|
103
126
|
## License
|
package/bin/dou-cl.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
* dou-cl —— 命令行斗地主 / 跑得快 / 510K
|
|
6
|
-
*
|
|
6
|
+
* 单机可玩;联网时「一个 ip:port 就是一个房间」。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const path = require('path');
|
|
@@ -27,12 +27,12 @@ function usage() {
|
|
|
27
27
|
console.log(' npx dou-cl -g paodekuai 跑得快');
|
|
28
28
|
console.log(' npx dou-cl -g fivek 510K');
|
|
29
29
|
console.log('');
|
|
30
|
-
console.log(colors.bold(' 联网'));
|
|
31
|
-
console.log(' npx dou-cl --serve
|
|
32
|
-
console.log(' npx dou-cl --serve --port
|
|
33
|
-
console.log(' npx dou-cl --
|
|
34
|
-
console.log(' npx dou-cl --join
|
|
35
|
-
console.log('
|
|
30
|
+
console.log(colors.bold(' 联网') + colors.dim('(一个 ip:port 就是一个房间)'));
|
|
31
|
+
console.log(' npx dou-cl --serve --port 8080 在 8080 开一个房间(默认斗地主)');
|
|
32
|
+
console.log(' npx dou-cl --serve --port 8080 -g fivek 指定房间玩法');
|
|
33
|
+
console.log(' npx dou-cl --serve --ports 10000-12000 一次开一片房间,每个端口一个');
|
|
34
|
+
console.log(' npx dou-cl --join 192.168.1.10:8080 连进这个房间');
|
|
35
|
+
console.log(colors.dim(' 服务器自己不占座位;人满了直接回绝,人不够就等着(s 让电脑补齐,q 退出)'));
|
|
36
36
|
console.log('');
|
|
37
37
|
console.log(colors.bold(' 摸鱼模式') + colors.dim('(默认开启,牌桌伪装成看起来正常的开发输出)'));
|
|
38
38
|
console.log(' npx dou-cl --skin hex 换皮肤: log(默认) / hex / json / diff');
|
|
@@ -42,6 +42,7 @@ function usage() {
|
|
|
42
42
|
console.log('');
|
|
43
43
|
console.log(colors.bold(' 其他'));
|
|
44
44
|
console.log(' --name <昵称> 联网时显示的名字');
|
|
45
|
+
console.log(' --host <地址> 服务器监听地址(默认 0.0.0.0)');
|
|
45
46
|
console.log(' --speed <毫秒> 单机电脑出牌间隔(默认 700)');
|
|
46
47
|
console.log(' --ascii 花色用 S/H/C/D 字母显示(老终端兼容)');
|
|
47
48
|
console.log(' -h, --help 显示帮助');
|
|
@@ -59,14 +60,36 @@ function usage() {
|
|
|
59
60
|
console.log('');
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
/** "8080" / "8080,8081" / "10000-12000" → [8080] / [8080,8081] / [10000..12000] */
|
|
64
|
+
function parsePorts(s) {
|
|
65
|
+
const out = [];
|
|
66
|
+
String(s || '').split(',').forEach(function (part) {
|
|
67
|
+
const p = part.trim();
|
|
68
|
+
if (!p) return;
|
|
69
|
+
const mm = /^(\d+)\s*-\s*(\d+)$/.exec(p);
|
|
70
|
+
if (mm) {
|
|
71
|
+
const a = Number(mm[1]);
|
|
72
|
+
const b = Number(mm[2]);
|
|
73
|
+
for (let i = Math.min(a, b); i <= Math.max(a, b); i++) {
|
|
74
|
+
if (i > 0 && i < 65536) out.push(i);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
const n = Number(p);
|
|
78
|
+
if (n > 0 && n < 65536) out.push(n);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
function parseArgs(argv) {
|
|
63
85
|
const o = {
|
|
64
86
|
mode: 'menu',
|
|
65
87
|
game: null,
|
|
66
88
|
server: process.env.DOUCL_SERVER || '127.0.0.1:8080',
|
|
67
|
-
code: null,
|
|
68
89
|
name: process.env.DOUCL_NAME || null,
|
|
69
90
|
port: Number(process.env.DOUCL_PORT) || 8080,
|
|
91
|
+
ports: null,
|
|
92
|
+
host: process.env.DOUCL_HOST || null,
|
|
70
93
|
speed: 700,
|
|
71
94
|
ascii: false,
|
|
72
95
|
skin: process.env.DOUCL_SKIN || require('../src/skins').DEFAULT_SKIN,
|
|
@@ -85,14 +108,13 @@ function parseArgs(argv) {
|
|
|
85
108
|
case '-v': case '--version': o.version = true; break;
|
|
86
109
|
case '--serve': case '--server-mode': o.mode = 'serve'; break;
|
|
87
110
|
case '--join': o.mode = 'join'; o.server = next(); break;
|
|
88
|
-
case '--
|
|
89
|
-
case '--create': o.mode = 'create'; o.game = next(); break;
|
|
111
|
+
case '--server': o.mode = 'join'; o.server = next(); break;
|
|
90
112
|
case '--local': o.mode = 'local'; o.game = next(); break;
|
|
91
|
-
case '-g': case '--game': o.mode = 'local'; o.game = next(); break;
|
|
92
|
-
case '--
|
|
93
|
-
case '--code': o.code = (next() || '').toUpperCase(); break;
|
|
94
|
-
case '--name': o.name = next(); break;
|
|
113
|
+
case '-g': case '--game': o.mode = o.mode === 'serve' ? 'serve' : 'local'; o.game = next(); break;
|
|
114
|
+
case '--ports': case '--port-range': o.ports = parsePorts(next()); break;
|
|
95
115
|
case '--port': o.port = Number(next()) || 8080; break;
|
|
116
|
+
case '--host': o.host = next(); break;
|
|
117
|
+
case '--name': o.name = next(); break;
|
|
96
118
|
case '--speed': o.speed = Number(next()) || 700; break;
|
|
97
119
|
case '--ascii': o.ascii = true; break;
|
|
98
120
|
case '--no-color': process.env.NO_COLOR = '1'; break;
|
|
@@ -118,6 +140,13 @@ function parseArgs(argv) {
|
|
|
118
140
|
case '-s':
|
|
119
141
|
o.skin = 'log';
|
|
120
142
|
break;
|
|
143
|
+
// 旧协议里的房间号 / 匹配 / 开房已经取消:地址本身就是房间
|
|
144
|
+
case '--code': case '--match': case '--create': {
|
|
145
|
+
console.error(colors.yellow('提示:' + a + ' 已经不需要了 —— 现在一个地址就是一个房间。'));
|
|
146
|
+
console.error(colors.dim(' 直接: npx dou-cl --serve --port 8080 然后别人 npx dou-cl --join <IP>:8080'));
|
|
147
|
+
if ((a === '--match' || a === '--create') && args[i + 1] && args[i + 1][0] !== '-') i++;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
121
150
|
default:
|
|
122
151
|
if (a && a[0] !== '-') {
|
|
123
152
|
if (a === 'exercise' || a === 'drill') { o.mode = 'exercise'; o.exercise = 'menu'; }
|
|
@@ -168,32 +197,23 @@ async function main() {
|
|
|
168
197
|
|
|
169
198
|
if (opts.mode === 'serve') {
|
|
170
199
|
const server = require('../src/net/server');
|
|
171
|
-
const
|
|
172
|
-
|
|
200
|
+
const game = opts.game || 'doudizhu';
|
|
201
|
+
const ports = (opts.ports && opts.ports.length) ? opts.ports : [opts.port];
|
|
202
|
+
const s = server.createServers({ game: game, ports: ports, host: opts.host || '0.0.0.0' });
|
|
203
|
+
console.log(colors.dim(' 服务端不占座位 —— 玩家执行 npx dou-cl --join <本机IP>:<端口> 进来'));
|
|
173
204
|
console.log(colors.dim(' Ctrl+C 停止'));
|
|
205
|
+
void s;
|
|
174
206
|
return;
|
|
175
207
|
}
|
|
176
208
|
|
|
177
|
-
if (opts.mode === '
|
|
209
|
+
if (opts.mode === 'join') {
|
|
178
210
|
const client = require('../src/net/client');
|
|
179
211
|
const io = new ui.IO();
|
|
180
|
-
if (opts.mode === 'join' && !opts.code) {
|
|
181
|
-
const code = await app.ask(io, ' 房间号(4 位)> ');
|
|
182
|
-
opts.code = code.toUpperCase();
|
|
183
|
-
}
|
|
184
|
-
if (opts.mode === 'create' && !opts.game) opts.game = 'doudizhu';
|
|
185
|
-
if (opts.mode === 'match' && !opts.game) opts.game = 'doudizhu';
|
|
186
212
|
if (!opts.name) {
|
|
187
213
|
const n = await app.ask(io, ' 你的昵称 > ');
|
|
188
214
|
opts.name = n || '玩家';
|
|
189
215
|
}
|
|
190
|
-
await client.runClient(io, {
|
|
191
|
-
mode: opts.mode,
|
|
192
|
-
game: opts.game,
|
|
193
|
-
code: opts.code,
|
|
194
|
-
name: opts.name,
|
|
195
|
-
server: opts.server,
|
|
196
|
-
}, opts);
|
|
216
|
+
await client.runClient(io, { server: opts.server, name: opts.name }, opts);
|
|
197
217
|
return;
|
|
198
218
|
}
|
|
199
219
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dou-cl",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "伪装成开发控制台的命令行牌桌 —— 斗地主 / 跑得快 / 510K
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "伪装成开发控制台的命令行牌桌 —— 斗地主 / 跑得快 / 510K,牌局藏在日志流、xxd、JSON 或 git diff 里;支持局域网联机,也有公共房间可直接和真人开局",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"doudizhu",
|
|
7
7
|
"斗地主",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"card-game",
|
|
13
13
|
"terminal",
|
|
14
14
|
"tui",
|
|
15
|
-
"stealth"
|
|
15
|
+
"stealth",
|
|
16
|
+
"multiplayer",
|
|
17
|
+
"lan"
|
|
16
18
|
],
|
|
17
19
|
"license": "MIT",
|
|
18
20
|
"author": "iguangyu",
|
package/src/app.js
CHANGED
|
@@ -127,7 +127,7 @@ function askHuman(io, engine, state, seat, key) {
|
|
|
127
127
|
if (skin) printStealthHelp(); else printHelp(key);
|
|
128
128
|
return step();
|
|
129
129
|
}
|
|
130
|
-
if (low === 'skins' || low === 'skin'
|
|
130
|
+
if (low === 'skins' || low === 'skin') { printSkinHelp(); return step(); }
|
|
131
131
|
if (low === 'quit' || low === 'exit') {
|
|
132
132
|
if (!skin) io.say(colors.dim('已退出本局。'));
|
|
133
133
|
io.close();
|
|
@@ -258,11 +258,8 @@ function showMenu(io, opts) {
|
|
|
258
258
|
io.say(' ' + colors.bold(m[0]) + ' ' + m[2] + ' ' + colors.dim(m[3]));
|
|
259
259
|
});
|
|
260
260
|
io.say('');
|
|
261
|
-
io.say(' ' + colors.bold('联机'));
|
|
262
|
-
io.say(' ' + colors.bold('4') + '
|
|
263
|
-
colors.bold('5') + ' 加入房间');
|
|
264
|
-
io.say(' ' + colors.bold('6') + ' 快速匹配 ' +
|
|
265
|
-
colors.bold('7') + ' 本机开服务器');
|
|
261
|
+
io.say(' ' + colors.bold('联机') + colors.dim('(一个 ip:port 就是一个房间)'));
|
|
262
|
+
io.say(' ' + colors.bold('4') + ' 加入房间 ' + colors.bold('5') + ' 开一个房间');
|
|
266
263
|
io.say('');
|
|
267
264
|
io.say(' ' + colors.bold('0') + ' 退出');
|
|
268
265
|
io.say(' ' + '\u2500'.repeat(58));
|
|
@@ -277,14 +274,14 @@ function showMenu(io, opts) {
|
|
|
277
274
|
.catch(function (e) { io.say(colors.red('出错: ' + e.message)); showMenu(io, opts); });
|
|
278
275
|
return;
|
|
279
276
|
}
|
|
280
|
-
if (c === '4'
|
|
281
|
-
|
|
277
|
+
if (c === '4') {
|
|
278
|
+
joinFlow(io, opts).catch(function (e) {
|
|
282
279
|
io.say(colors.red('出错: ' + e.message));
|
|
283
280
|
showMenu(io, opts);
|
|
284
281
|
});
|
|
285
282
|
return;
|
|
286
283
|
}
|
|
287
|
-
if (c === '
|
|
284
|
+
if (c === '5') { startServerFlow(io, opts); return; }
|
|
288
285
|
io.say(colors.red('没有这个选项:' + c));
|
|
289
286
|
setTimeout(function () { showMenu(io, opts); }, 600);
|
|
290
287
|
});
|
|
@@ -296,50 +293,40 @@ function ask(io, prompt) {
|
|
|
296
293
|
});
|
|
297
294
|
}
|
|
298
295
|
|
|
299
|
-
|
|
296
|
+
/** 联机 · 加入房间:问昵称 + 地址,地址本身就是房间 */
|
|
297
|
+
async function joinFlow(io, opts) {
|
|
300
298
|
io.clear();
|
|
301
299
|
io.say('');
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (choice === '1') {
|
|
305
|
-
io.say('');
|
|
306
|
-
io.say(' 选择玩法: 1 斗地主 2 跑得快 3 510K');
|
|
307
|
-
const g = await ask(io, ' > ');
|
|
308
|
-
const key = ({ '1': 'doudizhu', '2': 'paodekuai', '3': 'fivek' })[g.trim()] || 'doudizhu';
|
|
309
|
-
const srv = await ask(io, ' 服务器地址(直接回车 = 本机 ' + opts.server + ') > ') || opts.server;
|
|
310
|
-
io.close();
|
|
311
|
-
const client = require('./net/client');
|
|
312
|
-
await client.runClient(new ui.IO(), { mode: 'create', game: key, name: name, server: srv }, opts);
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (choice === '2') {
|
|
317
|
-
const code = await ask(io, ' 房间号(4 位) > ');
|
|
318
|
-
const srv = await ask(io, ' 服务器地址(直接回车 = 本机 ' + opts.server + ') > ') || opts.server;
|
|
319
|
-
io.close();
|
|
320
|
-
const client = require('./net/client');
|
|
321
|
-
await client.runClient(new ui.IO(), { mode: 'join', code: code.toUpperCase(), name: name, server: srv }, opts);
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
|
|
300
|
+
io.say(' ' + colors.bold('加入房间') + colors.dim(' 填对方的 ip:port 就行'));
|
|
325
301
|
io.say('');
|
|
326
|
-
io
|
|
327
|
-
const
|
|
328
|
-
const key = ({ '1': 'doudizhu', '2': 'paodekuai', '3': 'fivek' })[g.trim()] || 'doudizhu';
|
|
329
|
-
const srv = await ask(io, ' 服务器地址(直接回车 = 本机 ' + opts.server + ') > ') || opts.server;
|
|
302
|
+
const name = (await ask(io, ' 你的昵称 > ')) || '玩家';
|
|
303
|
+
const srv = (await ask(io, ' 房间地址 ip:port(回车 = ' + opts.server + ') > ')) || opts.server;
|
|
330
304
|
io.close();
|
|
331
305
|
const client = require('./net/client');
|
|
332
|
-
await client.runClient(new ui.IO(), {
|
|
306
|
+
await client.runClient(new ui.IO(), { server: srv, name: name }, opts);
|
|
333
307
|
}
|
|
334
308
|
|
|
335
|
-
|
|
309
|
+
/** 联机 · 本机开一个房间(服务器自己不占座位,别人用 --join 连进来) */
|
|
310
|
+
async function startServerFlow(io, opts) {
|
|
336
311
|
io.clear();
|
|
337
312
|
io.say('');
|
|
338
|
-
io.say('
|
|
313
|
+
io.say(' ' + colors.bold('开一个房间') + colors.dim(' 本机起一个房间,别人连进来'));
|
|
314
|
+
io.say('');
|
|
315
|
+
io.say(' 玩法: ' + colors.bold('1') + ' 斗地主 ' + colors.bold('2') + ' 跑得快 ' + colors.bold('3') + ' 510K');
|
|
316
|
+
const g = await ask(io, ' > ');
|
|
317
|
+
const key = ({ '1': 'doudizhu', '2': 'paodekuai', '3': 'fivek' })[g.trim()] || 'doudizhu';
|
|
318
|
+
const p = await ask(io, ' 端口(回车 = ' + opts.port + ') > ');
|
|
319
|
+
const port = Number(p) || opts.port;
|
|
320
|
+
|
|
321
|
+
const rules = rulesMod.get(key);
|
|
339
322
|
const server = require('./net/server');
|
|
340
|
-
server.
|
|
341
|
-
|
|
342
|
-
io.say(
|
|
323
|
+
server.createServers({ game: key, ports: [port] });
|
|
324
|
+
|
|
325
|
+
io.say('');
|
|
326
|
+
io.say(colors.dim(' 房间玩法:' + rules.name + ' 端口:' + port));
|
|
327
|
+
io.say(colors.dim(' 把地址发给朋友: npx dou-cl --join <你的IP>:' + port));
|
|
328
|
+
io.say(colors.dim(' 本机 IP 用 ipconfig 查;服务端自己不占座位。'));
|
|
329
|
+
io.say(colors.dim(' Ctrl+C 停止。'));
|
|
343
330
|
io.handler = null;
|
|
344
331
|
}
|
|
345
332
|
|