codeconnection
Version:
Code minecraft education edition using nodeJS. An attempt to improve on Microsoft Code Connection.
317 lines (252 loc) • 11.1 kB
JavaScript
//////////////////////////////////////////////////////////////////
// This is the web server ////////////////////////////////////////
// It takes the request for action and passes it to the //////////
// websocket for send and recieve from Minecraft /////////////////
//////////////////////////////////////////////////////////////////
const express = require('express')
const app = express()
//app gets json data sent to it
app.use(express.json());
let isGettingInventory = false;
let inventory = [];
//this processes the search from the main page
app.all(
"/",
async function (request, response) {
//send error if minecraft not connected
if (socket === undefined) {
let noconnection = {
body: {
statusMessage: "Minecraft not connected."
}
}
response.json(noconnection)
return
}
let command = request.body
let pieces = command.pieces
delete command.pieces
// console.log(command)
let minecraftResponse = {}
if (command.body.commandLine === "inventory") {
console.log('inventory')
isGettingInventory = true
inventory = []
for (let i = 0; i < 28; i++) {
inventory.push({})
}
// console.log('start')
//get agent position here
command.body.commandLine = "agent getposition"
pieces = 1
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
position = minecraftResponse.body.position
// console.log(position)
//get the items
for (let i = 1; i <= 27; i++) {
command.body.commandLine = "agent getitemcount " + i
pieces = 2
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
let data = (JSON.parse(minecraftResponse.body.properties.Result)).stackCount
inventory[i].count = data
if (inventory[i].count === 0) {
inventory[i].itemName = ''
inventory[i].slot = i
inventory[i].id = -1
inventory[i].count = 0
continue
}
// command.body.commandLine = "agent getitemdetail " + i
//drop item
// console.log(i)
command.body.commandLine = "agent drop " + i + " 1 up"
pieces = 2
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
// console.log(minecraftResponse)
//scan for item
command.body.commandLine = `/testfor @e[type=item,x=${position.x},y=${position.y},z=${position.z},dx=${2},dy=${2},dz=${2}]`
pieces = 1
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
let items = minecraftResponse.body.victim
//collect
command.body.commandLine = `agent collect all`
pieces = 2
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
data = (JSON.parse(minecraftResponse.body.properties.Result)).itemName
inventory[i].itemName = items[0]
inventory[i].slot = i
}
minecraftResponse = inventory
} else {
isGettingInventory = false;
//default
minecraftResponse = await new Promise((resolve, reject) => {
resolveMap.set(command.header.requestId,
{
resolve: resolve,
pieces: pieces
}
)
socket.send(JSON.stringify(command))
})
}
response.json(minecraftResponse)
}
)
app.listen(3001)
/////////////////////////////////////////////////////
//// SOCKET /////////////////////////////////////////
/////////////////////////////////////////////////////
//https://www.youtube.com/watch?v=bjanNXXwQbo
//need to turn off encrypted websockets
//hide HUD in video settings (because don't want to see all the command info on the screen)
const WebSocket = require('ws') //to create the websocket
const uuid = require('uuid') //to generate unique message ids
console.log('On minecraft send command "/connect localhost:3000"') //message to user what to do
const wss = new WebSocket.Server({ port: 3000 }) //creates the socket server
let resolveMap = new Map() //used to store all the promises that commands make
//these are resolved when the messages come back from Minecraft
let socket; //need to extract the socket
wss.on(
'connection', //runs when Minecraft connects to the server
async function connection(_socket) {
console.log('Minecraft connection established.')
socket = _socket
//subscribe to player messages
socket.send(
JSON.stringify(
{
"header": {
"version": 1,
"requestId": uuid.v4(),
"messageType": "commandRequest",
"messagePurpose": "subscribe"
},
"body": {
"eventName": "PlayerMessage"
}
}
)
)
//subscribe to agentcommands
socket.send(
JSON.stringify(
{
"header": {
"version": 1,
"requestId": uuid.v4(),
"messageType": "commandRequest",
"messagePurpose": "subscribe"
},
"body": {
"eventName": "AgentCommand"
}
}
)
)
//re cieves the messages back from Minecraft
//messages are parsed to find the requestID
//the promise for this is found in the map and resolved
//the record is then removed
let piecesLeft = 0
let pendingResolution;
socket.on(
'message',
(packet) => {
let response = JSON.parse(packet)
// console.log(response)
// console.log(response)
if (isGettingInventory) {
if (piecesLeft !== 0) {
piecesLeft--
if (piecesLeft === 0) {
pendingResolution(response)
}
} else if (response.header.requestId) {
if (resolveMap.get(response.header.requestId)) {
let resolution = resolveMap.get(response.header.requestId)
if (resolution.pieces !== 1) {
pendingResolution = resolution.resolve
piecesLeft = resolution.pieces - 1
} else {
resolution.resolve(response)
}
resolveMap.delete(response.header.requestId)
}
}
} else {
//default
//need piecesLeft because a lot of commands get two response packets from Minecraft
//and the second packet has the important information in it
if (piecesLeft !== 0) {
piecesLeft--
if (piecesLeft === 0) {
pendingResolution(response)
}
} else if (response.header.requestId) {
if (resolveMap.get(response.header.requestId)) {
let resolution = resolveMap.get(response.header.requestId)
// console.log(response.body.statusMessage)
// console.log(response.body.statusMessage.includes('fail'))
if (response.body.statusMessage.includes('fail') || response.body.statusMessage.startsWith("Cannot issue command")) {
resolution.pieces = 1
piecesLeft = 0
response.body.properties = {}
response.body.properties.Result = JSON.stringify({ success: false })
}
if (resolution.pieces !== 1) {
pendingResolution = resolution.resolve
piecesLeft = resolution.pieces - 1
} else {
// console.log(response)
resolution.resolve(response)
}
resolveMap.delete(response.header.requestId)
}
}
}
}
)
}
)