这两天学习了nodejs,于是想着做点什么来实践一下,正好对Minecraft 基岩版的websocket功能比较感兴趣,于是做了一个websocket服务器的demo,可以用来获得游戏内的信息以及执行命令。
不少人已经在bds服务器上尝试过使用wsserver/connect命令了吧?但是这条命令不知道为什么是不能直接使用的,要想使用这条命令我们需要做以下准备。
做完上述操作之后就可以使用/connect命令了,当然如果只是输入/connect,会提示Command version mismatch,这里我们暂时不管它。
我使用的使nodejs+typescript的方式创建了这个demo,只有3个文件。注释已经写的很清楚了,有问题就留下评论吧。
app.ts server.ts packet.ts
//app.ts 用于启动程序,端口可以在这里设置
import {WSServer} from './server';
let server = new WSServer();
server.listen(6800);</code></pre>
//server.ts 包含ws服务器类,用于创建server对象
import { Packet,UnSubscribe,Subscribe,CommandPacket } from './packet/packet';
import { Server } from 'ws';
import {EventEmitter} from 'events';
import { Socket } from 'net';
import {createInterface} from 'readline';
//import {v4} from 'uuid';
let uuid4 = require('uuid/v4');
//创建一个负责输入输出的对象
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
export class WSServer extends EventEmitter{
//strictPropertyInitialization:false
_socket:Server;
public listen(port:number):void{
let server:WSServer = this;
this._socket = new Server({port:port});
//当有客户端连接时回调
console.log("WS开始监听" + port);
this._socket.on("connection",socket => {
console.log("客户端连接");
//发送subscribe包建立监听
registerSubscribe(socket,"PlayerMessage");
registerSubscribe(socket,"BlockPlaced");
//当socket收到信息时回调
socket.on("message", message => {
console.log("接收到客户端的信息");
let data = JSON.parse(message as string);
let msgPurpose = data.header.messagePurpose;
if(msgPurpose == "error"){
console.log("出现错误:", data);
}
else if(msgPurpose == "event"){
console.log(data.body.eventName);
//发送unsubscribe包,取消对该事件的监听,具体表现为服务端只能获得一次客户端的方块放置事件
if (data.body.eventName == "BlockPlaced"){
sendCommand(socket,<code>say 发送unsubscribe包解除对方块放置事件的监听</code>);
unRegisterSubscribe(socket,"BlockPlaced");
}
}
else if(msgPurpose == "commandResponse"){
console.log("命令返回:" + data.body.statusCode);
}
});
//接收到控制台的发送信息事件
server.on("sendMsg",msg=>{
console.log("[sendMsg]:" + msg);
sendCommand(socket,<code>say ${msg}</code>);
});
socket.on("error",err=>{
console.log("建立的socket出现错误" + err.message);
});
socket.on("close", () => {console.log("客户端断开连接")});
});
this._socket.on("error",error=>{
console.log(<code>出现错误${error}</code>);
});
//持续获得用户输入
rl.on('line', (input) => {
console.log(<code>[consoleInput]:${input}</code>);
let [cmd,content] = input.split(":");
if(cmd == "send"){
//触发sendMsg事件
server.emit("sendMsg",content);
}
else if(cmd == "exit"){
process.exit();
}
});
}
}
function registerSubscribe(socket:any,eventName:string):void{
let packet:Subscribe = new Subscribe(eventName);
socket.send(JSON.stringify(packet));
}
function unRegisterSubscribe(socket:any,eventName:string):void{
let packet:UnSubscribe = new UnSubscribe(eventName);
socket.send(JSON.stringify(packet));
}
function sendCommand(socket:any,command:string):void{
let packet:CommandPacket = new CommandPacket(command);
socket.send(JSON.stringify(packet));
}</code></pre>
<pre><code class="language-javascript">//packet 这里是三种数据包的类
let uuid4 = require('uuid/v4');
//数据包
export abstract class Packet{
body:{};
header:{};
}
export class Subscribe extends Packet{
body:{
eventName: string;
};
header:{
requestId: string;
messagePurpose: string;
version: number;
messageType: string;
};
constructor(eventName:string){
super();
this.body = {
eventName:eventName
};
this.header = {
requestId: uuid4(),
messagePurpose: "subscribe",
version: 1,
messageType: "commandRequest"
};
}
}
export class UnSubscribe extends Packet{
body:{
eventName: string;
};
header:{
requestId: string;
messagePurpose: string;
version: number;
messageType: string;
}
constructor(eventName:string,uuid = uuid4()){
super();
this.body = {
eventName: eventName
};
this.header = {
requestId: uuid,
messagePurpose: "unsubscribe",
version: 1,
messageType: "commandRequest"
}
}
}
export class CommandPacket extends Packet{
body:{};
header:{};
constructor(cmd:string){
super();
this.body = {
origin:
{
type: "player"
},
commandLine: cmd,
version: 1
};
this.header = {
requestId: uuid4(),
messagePurpose: "commandRequest",
version: 1,
messageType: "commandRequest"
};
}
}
如果想部署一个ws服务器试试看
git clone https://github.com/aoyouer/BedrockWsServer.git
cd BedrockWsServer
npm install
node build/app.js
#默认使用6800端口```
之后我们在游戏里即可使用 /connect ip:6800
连接ws服务器了,但是不知道为什么,我在自己的电脑(win10)上运行的ws服务器可以通过浏览器访问,但是无法在游戏中连接,在我将程序放到远程的服务器上才正常连接。
在这个demo中,我尝试发送/接收了mc中文wiki ws教程中提到的几种数据包
在后台输入exit退出程序。
写这个小程序算是加深了一些我对nodejs的了解,Minecraft基岩版的websocket虽然看上去可以获得很多信息,但是在我看来,它的功能还是相当有限的,因为有几个巨大的限制
因为这些限制,我暂时想不出什么有趣的应用方向(也许之后我会慢慢扩展这个项目),不过github上依旧有几个比较成熟的基于Minecraft websocket的项目可以了解一下