UNPKG

codeconnection

Version:

Code minecraft education edition using nodeJS. An attempt to improve on Microsoft Code Connection.

1,480 lines (1,164 loc) 47.5 kB
/////////////////////////////////////////////////////////// //// This send the commands to the web server ///////////// /////////////////////////////////////////////////////////// const fetch = require('sync-fetch') const uuid = require('uuid') const db = require('siennasql') const { blockNames } = require('./crafting/blockListNames') const { recipeList } = require('./crafting/masterRecipeList') const { blocks } = require('./crafting/blockList') const prompt = require('prompt-sync')() db.connect(__dirname+'/mc2.db') //////////////////////////////////////////////////////////// /// This function transmits the command that was generated as a JSON object to the server and awaits the response ///////////////////////////////////////////////////////////// function transmit(cmd, pieces = 1) { let commandToSend = command(cmd) commandToSend.pieces = pieces let response try { response = fetch('http://localhost:3001', { method: 'POST', body: JSON.stringify(commandToSend), headers: { 'Content-type': 'application/json; charset=UTF-8' } } ).json() } catch (error) { if (error.code === "ECONNREFUSED") { console.log("\nThe server has not been started.\n1. Run 'npx codeserver-start' to launch.\n2. Then in Minecraft, execute the '/connect localhost:3000' command.\n3. Then run your code again.\n") } else { console.log(error) } process.exit(0) } //this is where handle the issue of agent being out of range let canCheck = true while (response.body && canCheck) { if (response.body.statusMessage) { if (response.body.statusMessage === "Minecraft not connected.") { console.log("\nMINECRAFT IS NOT CONNECTED TO THE SERVER.\n\n1. Run '/connect localhost:3000' on minecraft\n2. Then re-run your code.\n ") process.exit(0) } if (response.body.statusMessage.startsWith("Cannot issue command")) { if (agent.autocorrect) { let position = agent.getPosition() transmit(`/tp @s ${position.x + 5} ${position.y + 7} ${position.z + 5} facing @c`) response = fetch('http://localhost:3001', { method: 'POST', body: JSON.stringify(commandToSend), headers: { 'Content-type': 'application/json; charset=UTF-8' } } ).json() } else { let input = prompt("\n\n----- Agent Error Message -----\nProgram paused, agent is out of range. Reposition the player closer and then hit any key to resume.\nEntering 'c' will terminate the program.\n") if (input.toLowerCase() === 'c') { process.exit(0) } response = fetch('http://localhost:3001', { method: 'POST', body: JSON.stringify(commandToSend), headers: { 'Content-type': 'application/json; charset=UTF-8' } } ).json() } } else { canCheck = false } } else { canCheck = false } } return response } //creates the JSON for minecraft commands to control things function command(cmd) { const msg = { "header": { "version": 1, "requestId": uuid.v4(), // Send unique ID each time "messagePurpose": "commandRequest", "messageType": "commandRequest" }, "body": { "version": 1, "commandLine": cmd, // Define the command "origin": { "type": "player" // Message comes from player } } } return msg } ////////////////////////////////////////////////////////////////////// ////these functions are about setting directions and stuff ////////////////////////////////////////////////////////////////////// const SOUTH = -180 //-180 z decreases const WEST = -90 //-90 x decreases const NORTH = 0 //0 z increases const EAST = 90 //90 x increases function getDirection(value) { if (value === SOUTH) { return 'SOUTH' } else if (value === NORTH) { return 'NORTH' } else if (value === EAST) { return 'EAST' } else if (value === WEST) { return 'WEST' } } ///////////////////////////////////////////////////////////////////////// ////// THIS IS THE INVENTORY CONTROLLER ///////////////////////////////// ////// and one day crafting machine ///////////////////////////////////// ///////////////////////////////////////////////////////////////////////// class INVENTORY { //0th element is empty //this is because the game runs things as slots 1 to 27 constructor() { //this store the actual items this.items = [] this.empty() } //empties inventory empty() { this.items.length = 0 for (let i = 0; i <= 27; i++) { let item = {} item.itemName = '' item.slot = i item.count = 0 this.items.push(item) } } //match inventory with game inventory //this needs updating to a better method //it returns the mineracft name of the items from the game e.g. stone, but not detail for the actual type e.g. granite, diorite etc sync(slot) { let start = 1; //inventory slots are numbered 1 to 27 in minecraft let end = 27; if (slot !== 'all') { start = slot end = slot + 1 } let inventory = [] if (slot === 'all') { console.log("Checking the entire inventory takes some time...") inventory = transmit("inventory") //it is 30% faster to do this way then check individually } else { let position = agent.getPosition() for (let i = start; i < end; i++) { agent.drop(i, 1, d.UP) let data = transmit(`/testfor @e[type=item,x=${position.x},y=${position.y},z=${position.z},dx=${2},dy=${2},dz=${2}]`) let items = data.body.victim if (items === undefined) { break } let itemName = items[0] agent.collect() // let response = transmit('agent getitemdetail ' + i, 2) // let itemName = JSON.parse(response.body.properties.Result).itemName let count = 0, space = 64; if (itemName !== "") { let response = transmit('agent getitemcount ' + i, 2) count = JSON.parse(response.body.properties.Result).stackCount space = 64 - count } inventory.push( { slot: i, name: itemName, stackCount: count } ) if (inventory.length % 3 === 0 && slot === 'all') { console.log(Math.round(inventory.length / 27 * 100) + "% done...") } } } if (slot === 'all') { console.log('Inventory complete.') this.items = inventory } else { this.items[slot] = inventory[0] } for (let i = 0; i < inventory.length; i++) { if (inventory[i].itemName !== undefined) { if (inventory[i].itemName !== '') { let id = db.run("SELECT NUMERICAL_ID FROM BLOCKS WHERE SCAN_NAMES=?", [inventory[i].itemName]) inventory[i].id = id[0].NUMERICAL_ID } else { inventory[i].id = -1 } } } return inventory } //reveal stored inventory list() { return this.items } //move items between slots transfer() { } //pick up items from the ground and file them in the inventory collect(type) { // if (type==='all'){ this.sync('all') //anything could have happened /* } else { //need to tie the blocks list to the database //do we have item already //check that slot and then the next empty slot (hopefully it fills the inventory in a logical order) for (let i=1; i<this.items.length; i++){ } }*/ } //drop from a single slot up to but not exceeding the limit drop(slot, quantity) { let count = this.items[slot].count - quantity if (count < 0) { count = 0 } this.items[slot].count = count } //craft new items from existing ones //this is a very hacky way of giving the agent the ability to craft things //it // gets the desired item // flattens the inventory // sees if the inventory matches the requirements for the desired item recipe // drops the ingredients of the desired item // clears the dropped ingredients from the area // inserts the desired object above the agent // agent breaks the item // agent collects the broken object and stores it in the inventory // //this is return a list of missing items or an empty list if the transaction is successful craft(desiredItem = 1, quantity = 1) { quantity = Math.round(quantity) if (quantity < 1 || isNaN(quantity)) { console.log('Must craft at least one item.') return false } //this is where we work out if the crafting is possible //1. is desired item real if (!blockNames[desiredItem]) { return false } //2. what are the requirements let itemNumber = blockNames[desiredItem] // console.log(desiredItem) let potentialRecipes = recipeList[desiredItem].recipe // console.log(potentialRecipes) //3. check inventory console.log('Checking Inventory') let stackedInventory = this.direct('inventory') // console.log(stackedInventory) //flatten the inventory let flatInventory = [] for (let i = 1; i < stackedInventory.length; i++) { let slot = stackedInventory[i] for (let count = 0; count < slot.count; count++) { flatInventory.push(blockNames[slot.itemName]) } } //4. validate requirements //setup a null variable to store the recipe that could be made with the current items in the inventory let successfulRecipe = null; //iterate over the potential recipes for the desired object and choose the first successful one for (let i = 0; i < potentialRecipes.length; i++) { let recipe = potentialRecipes[i] // console.log(flatInventory) let hasEnoughMaterials = recipe.every(val => flatInventory.includes(val) && flatInventory.filter(el => el === val).length >= recipe.filter(el => el === val).length ); //if found a good recipe, then save it and move on if (hasEnoughMaterials) { successfulRecipe = recipe break //choose the first successful recipe } } //print results if (successfulRecipe !== null) { // console.log('good') // console.log(successfulRecipe) } else { console.log('Cannot craft the desired item') //need to print list of missing items let missingItems = [] let missingItemsText = [] let sampleRecipe = potentialRecipes[0] for (let i = 0; i < sampleRecipe.length; i++) { if (flatInventory.indexOf(sampleRecipe[i]) !== -1) { flatInventory[flatInventory.indexOf(sampleRecipe[i])] = undefined } else { missingItems.push(sampleRecipe[i]) missingItemsText.push(blockNames[sampleRecipe[i]]) } } console.log(`You are missing ${missingItemsText} (${missingItems})`) return missingItems } //5. work out most of requested item that can be created (up to the value requested) //and subsequently adjust (if required) the quantity limit let canCreate = 0 for (let i = 0; i < quantity; i++) { let made = true for (let j = 0; j < successfulRecipe.length; j++) { if (flatInventory.indexOf(successfulRecipe[j]) !== -1) { flatInventory[flatInventory.indexOf(successfulRecipe[j])] = undefined } else { made = false } } if (made) { canCreate++ } else { break } } console.log(`Can create ${canCreate}/${quantity} of the requested item`) quantity = canCreate //5.5 work out the list of drops //generate list of required items let allIngredients = [] for (let i = 0; i < quantity; i++) { allIngredients = allIngredients.concat(successfulRecipe) } allIngredients.sort() let droppingList = [] droppingList.length = 28 droppingList.fill(0) for (let i = 0; i < allIngredients.length; i++) { //find right list let slot = 0 while (stackedInventory[slot].itemName !== blockNames[allIngredients[i]]) { slot++ } droppingList[slot]++ stackedInventory[slot].count-- if (stackedInventory[slot].count === 0) { stackedInventory[slot].itemName = '' } } // console.log(droppingList) //6. drop the items //this is where the items to be removed are dropped/lost // this.dropAll() for (let i = 0; i < droppingList.length; i++) { if (droppingList[i] !== 0) { let drop = transmit(`agent drop ${i} ${droppingList[i]} up`) } } //6.5 adjust the cached inventory //7. delete dropped items //this is how all the ejected items are removed let p = this.getPosition() let response = transmit(`/kill @e[type=item,x=${p.x - 2},y=${p.y - 2},z=${p.z - 2},dx=5,dy=5,dz=5]`) //8. spawn the requested item for (let i = 0; i < quantity; i++) { //this is how the entity is given an object in exchange //create the object above, destory it and collect it // console.log(`/setblock ${p.x} ${p.y + 1} ${p.z} ${blockNames[desiredItem+'']}`) response = transmit(`/setblock ${p.x} ${p.y + 1} ${p.z} ${blockNames[desiredItem + '']}`) this.destroy('up') //need to run collect twice for some reason (I think it is a timing thing in Minecraft) this.collect() this.collect() } return true } } let agentInventory = new INVENTORY() ///////////////////////////////////////////////////////////////////////// //////// AGENT STATS MACHINE //////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// let timer = new Date() let agentStats = { move: 0, turn: 0, inventory: 0, place: 0, destroy: 0, collect: 0, getPosition: 0, inspect: 0, till: 0, attack: 0, drop: 0, detect: 0, detectRedstone: 0, transfer: 0, face: 0, storePosition: 0, restorePosition: 0, radar: 0 } ///////////////////////////////////////////////// ////// THIS IS THE AGENT //////////////////////// ///////////////////////////////////////////////// class AGENT { constructor() { this.x; this.y; this.z; this.direction; this.autocorrect = true } //move the player to be close to the player if distance too far to execute command followed(tf) { if (tf) { this.autocorrect = true } else { this.autocorrect = false } } //this can run any slash command sent through direct(cmd, count = 1) { let response = transmit(cmd, count) return response } //get agent position in world and store the coordinates getPosition() { agentStats.getPosition++ let response = transmit('/querytarget @c', 1) // console.log(response) let data = JSON.parse(response.body.details)[0] let position = data.position //return p /* let response = transmit('agent getposition') if (response.body.properties !== undefined) { if (response.body.properties.Result === '{"success":false}') { return false } } */ let x = position.x let y = position.y let z = position.z let direction = response.body["y-rot"] return { x: x, y: y, z: z, direction: getDirection(direction) } } getPlayerPosition() { let response = transmit('/querytarget @s', 1) let data = JSON.parse(response.body.details)[0] let p = data.position return p } //get direction faced orientation() { let response = transmit('agent getposition') this.direction = response.body["y-rot"] return getDirection(this.direction) } //move forward move(direction = 'forward', distance = 1) { agentStats.move++ if (!validateDirection(direction)) { if (!isNaN(direction)) { distance = Math.round(direction) if (distance < 1) { distance = 1 } direction = 'forward' } else { console.log("Invalid move command.") return false } } let success = true; for (let i = 0; i < distance; i++) { let response = transmit('agent move ' + direction, 2) success = JSON.parse(response.body.properties.Result).success if (!success) { break; //take take QCAA } } return success } //turn left or right turn(direction = 'left', times = 1) { agentStats.turn++ if (direction !== 'left' && direction !== 'right') { if (!isNaN(direction)) { times = Math.round(direction) if (times < 1) { times = 1 } direction = 'left' } else { console.log("Invalid turn command.") return false } } let success = true for (let i = 0; i < times; i++) { let response = transmit('agent turn ' + direction) // success = JSON.parse(response.body.properties.Result).success // if (!success) { // break; //take take QCAA // } } return success } //detect object inspect(direction = 'forward') { agentStats.inspect++ if (!validateDirection(direction)) { console.log("Invalid inspect command") return false } let response = transmit('agent inspect ' + direction, 2) let blockName = JSON.parse(response.body.properties.Result).blockName return blockName } //destroy object in direction destroy(direction = "forward") { agentStats.destroy++ if (!validateDirection(direction)) { console.log("Invalid destroy command") return false } let response = transmit('agent destroy ' + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //collect 'all' objects or object of specific name that is near the agent collect(type = 'all') { agentStats.collect++ let response; if (type === 'all') { response = transmit('agent collect ' + type, 2) } else { response = transmit('agent collect ' + type, 2) } let success = JSON.parse(response.body.properties.Result).success return success } //returns full inventory //or that for a single slot //takes 5-6 seconds inventory(slot = 'all') { agentStats.inventory++ let inventory; if (slot === 'all') { inventory = agentInventory.sync('all') } else { if (isNaN(slot)) { slot = 1 } else { slot = Math.round(slot) if (slot < 1) { slot = 1 } if (slot > 27) { slot = 27 } } inventory = agentInventory.sync(slot) } return inventory } //till the soil (agent has an automatic hoe-ing ability) till(direction = "forward") { agentStats.till++ if (direction !== 'forward' && direction !== 'back' && direction !== 'left' && direction !== 'right') { console.log("Invalid till command") return false } let response = transmit('agent till ' + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //attack attack(direction = "forward") { agentStats.attack++ if (!validateDirection(direction)) { console.log("Invalid attack command") return false } let response = transmit('agent attack ' + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //drops all items in the direction specified dropAll(direction = 'forward') { agentStats.drop++ if (!validateDirection(direction)) { console.log("Invalid dropall command") return false } let response = transmit('agent dropall ' + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //this works ok //seems to want to drop from every slot containing items of the same type drop(slot = 1, quantity = 1, direction = 'forward') { agentStats.drop++ if (isNaN(slot) || isNaN(quantity) || !validateDirection(direction)) { console.log("Invalid drop command") return false } slot = Math.round(slot) if (slot < 1) { slot = 1 } if (slot > 27) { slot = 27 } quantity = Math.round(quantity) if (quantity < 1) { quantity = 1 } let response = transmit('agent drop ' + slot + " " + quantity + " " + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //places a block place(direction = 'forward', slot = 1) { agentStats.place++ if (!validateDirection(direction)) { if (!isNaN(slot)) { slot = Math.round(slot) if (slot < 1) { slot = 1 } if (slot > 27) { slot = 27 } direction = 'forward' } else { console.log("Invalid place command.") return false } } let response = transmit('agent place ' + slot + " " + direction, 2) let success = JSON.parse(response.body.properties.Result).success return success } //use is a synonym for 'place' because it makes more sense sometimes use(direction = 'forward', slot = 1) { place(direction, slot) } //detects if there is a colliable (blocking) block in the direction detect(direction = "forward") { agentStats.detect++ if (!validateDirection(direction)) { console.log('Invalid detect command') return false } let response = transmit("agent detect " + direction, 2) let result = JSON.parse(response.body.properties.Result).result return result } //return the block name inspect(direction = "forward") { agentStats.inspect++ if (!validateDirection(direction)) { console.log('Invalid inspect command') return false } let response = transmit("agent inspect " + direction, 2) let success = JSON.parse(response.body.properties.Result).success if (success) { let blockName = JSON.parse(response.body.properties.Result).blockName // console.log(blockName) let fullName; let query = "SELECT * FROM BLOCKS WHERE AGENT_INSPECT = ? OR LEGACY_ITEM_ID=? AND LEGACY_ITEM_ID IS NOT NULL ORDER BY DATA_VALUE ASC" let list = db.run(query, [blockName, blockName]) if (list.length > 1) { let position = agent.getPosition() // console.log(position.direction) if (position.direction === 'SOUTH') { if (direction === d.FORWARD) { position.z-- } if (direction === d.BACK) { position.z++ } if (direction === d.LEFT) { position.x-- } if (direction === d.RIGHT) { position.x++ } if (direction === d.UP) { position.y++ } if (direction === d.DOWN) { position.y-- } } if (position.direction === 'NORTH') { if (direction === d.FORWARD) { position.z++ } if (direction === d.BACK) { position.z-- } if (direction === d.LEFT) { position.x++ } if (direction === d.RIGHT) { position.x-- } if (direction === d.UP) { position.y++ } if (direction === d.DOWN) { position.y-- } } if (position.direction === 'EAST') { if (direction === d.FORWARD) { position.x-- } if (direction === d.BACK) { position.x++ } if (direction === d.LEFT) { position.z++ } if (direction === d.RIGHT) { position.z-- } if (direction === d.UP) { position.y++ } if (direction === d.DOWN) { position.y-- } } if (position.direction === 'WEST') { if (direction === d.FORWARD) { position.x++ } if (direction === d.BACK) { position.x-- } if (direction === d.LEFT) { position.z-- } if (direction === d.RIGHT) { position.z++ } if (direction === d.UP) { position.y++ } if (direction === d.DOWN) { position.y-- } } for (let i = 0; i < list.length; i++) { // console.log(i, list[i]) let outcome = transmit(`/testforblock ${position.x} ${position.y} ${position.z} ${blockName} ${list[i].DATA_VALUE}`) // console.log(outcome) if (outcome.body.statusMessage.startsWith("Success")) { blockName = list[i].NUMERICAL_ID fullName = list[i].NAME i = list.length // break } } } else if (list.length === 1) { blockName = list[0].NUMERICAL_ID fullName = list[0].NAME } else { return false3 } let name = blockNames[blockName] let id = blockName if (name === undefined) { id = JSON.parse(response.body.properties.Result).blockName name = blockNames[name] if (name === undefined) { name = '' } } if (isNaN(name)) { return { fullName: fullName, name: name, id: id } } else { return { fullName: fullName, name: id, id: name } } } return success } //is block recieving redstone power in the specified direction detectRedstone(direction = 'forward') { agentStats.detectRedstone++ if (!validateDirection(direction)) { console.log('Invalid inspect command') return false } let response = transmit("agent detectredstone " + direction, 2) let result = JSON.parse(response.body.properties.Result).result return result } //transfer inventory from one slot to another transfer(from = 1, to = 1, quantity = 0) { agentStats.transfer++ if (isNaN(from) || isNaN(to) || isNaN(quantity)) { console.log("Invalid transfer command") return false } from = Math.round(from) to = Math.round(to) quantity = Math.round(quantity) if (from < 1 || from > 27) { return false } if (to < 1 || to > 27) { return false } let response = transmit("agent transfer " + from + " " + quantity + " " + to, 2) let success = JSON.parse(response.body.properties.Result).success return success } // tp to player tpToPlayer() { let position = agent.getPosition() let response = transmit("/tp @c @s", 1) let newPosition = agent.getPosition() let dx = Math.abs(position.x - newPosition.x) let dy = Math.abs(position.y - newPosition.y) let dz = Math.abs(position.z - newPosition.z) agentStats.move = agentStats.move + dx + dy + dz - 2 return true; } //custom destroy, collect and move command (these often happen in this sequence) destroyCollectMove() { this.destroy() this.collect() this.move() } //face in a certain direction face(direction = NORTH) { agent.face++ if (direction !== NORTH && direction !== SOUTH && direction !== EAST && direction !== WEST) { console.log("Invalid face command") return false } let response = transmit('agent getposition') while (response.body["y-rot"] !== direction) { this.turn('left') response = transmit('agent getposition') } } //same as position() but makes more sense to a person storePosition() { agentStats.storePosition++ let response = transmit('agent getposition') this.x = response.body.position.x this.y = response.body.position.y this.z = response.body.position.z this.direction = response.body["y-rot"] return { x: this.x, y: this.y, z: this.z, direction: getDirection(this.direction) } } //this puts the agent back in the last stored position restorePosition() { agentStats.restorePosition++ let position = agent.getPosition() let response = transmit(`/tp @c ${this.x} ${this.y} ${this.z}`) let dx = Math.abs(position.x - this.x) let dy = Math.abs(position.y - this.y) let dz = Math.abs(position.z - this.z) agentStats.move = agentStats.move + dx + dy + dz - 1 response = transmit('agent getposition') while (response.body["y-rot"] !== this.direction) { this.turn('left') response = transmit('agent getposition') } } //have the agent craft things // experimental /*craft(desiredItem = 1, quantity = 1) { return agentInventory.craft(desiredItem, quantity) }*/ radar(target) { agentStats.radar++ if (isNaN(target)) { return [] } let position = agent.getPosition() let px = position.x let y = position.y let pz = position.z let sx = Math.floor(px / 16) let sz = Math.floor(pz / 16) let dx = 1, dz = 1; if (px - sx * 16 >= 8) { dx = 0 } if (pz - sz * 16 >= 8) { dz = 0 } let colourCode = db.run('SELECT RADAR FROM BLOCKS WHERE NUMERICAL_ID=?', [target])[0] if (colourCode === null) { return false } colourCode = colourCode.RADAR let height; let potentials = [] //run the queries for (let x = sx - dx; x <= sx + 1 - dx; x++) { for (let z = sz - dz; z <= sz + 1 - dz; z++) { let data = agent.direct(`/getchunkdata overworld ${x} ${z} ${y + 1}`, 1) let input = data.body.data.replaceAll("\"", '') let line = input.split(',') //unzip stage let line2 = [] for (let i = 0; i < line.length; i++) { let code = line[i] if (code.indexOf('*') !== -1) { let values = code.split('*') for (let j = 0; j <= values[1]; j++) { line2.push(values[0]) } } else { line2.push(code) } } //un-dictionary stage for (let i = 0; i < line2.length; i++) { if (!isNaN(line2[i])) { line2[i] = line2[line2[i]] } } //filter results to match search for (let i = 0; i < line2.length; i++) { if (height === undefined) { let test = line2[i].substring(4, 6) const buffer = Buffer.from(test, 'base64'); const bufString = buffer.toString('hex'); let value = parseInt(bufString, 16) if (value === y) { height = test } } if (height === undefined) { //not found correct hheight yet so must be an air block line2[i] = '' } else { //delete record if not a match if (!line2[i].endsWith(height)) { line2[i] = '' } else { if (!line2[i].startsWith(colourCode)) { line2[i] = '' } else { //potential match let detail = {} detail.x = i % 16 + x * 16 detail.y = y detail.z = Math.floor(i / 16) + z * 16 potentials.push(detail) } } } } } } //remove any potentials that are more that 15 blocks away from the co-ordinates in any direction for (let i = potentials.length - 1; i >= 0; i--) { let c = potentials[i] if (Math.abs(c.x - px) > 15 || Math.abs(c.z - pz) > 15) { potentials.splice(i, 1) } } return potentials } getStats() { let total = 0 Object.keys(agentStats).forEach((key) => { total += agentStats[key] }) agentStats.total = total let time = new Date() agentStats.time = (time.getTime() - timer.getTime()) / 1000 return agentStats } give(block, slot = 1, quantity = 1, index=0) { let info = db.run("SELECT GIVE FROM BLOCKS WHERE NUMERICAL_ID=?", [block]) if (info.length > 1) { console.log("\nUh oh, duplicate id found in database.\nTrying to give you the first returned item and hoping it is correct.\nThe developer sknow these problems exist and will try and fix them over time.\nSorry for the inconvenience.\n") console.log("\nIf the delivered block isn't right, add a fourth parameter to the 'give' as an integer greater than or equal to 1 and less than "+info.length+" for this item and that will attempt to place a different item in the inventory.\n\n") //console.log(block) // return false } if (isNaN(slot)) { slot = 1 } if (isNaN(quantity)) { quantity = 1 } slot = Math.round(slot) quantity = Math.round(quantity) if (slot < 1) { slot = 1 } if (slot > 27) { slot = 27 } if (quantity < 1) { quantity = 1 } if (quantity > 64) { quantity = 64 } //game names slots 1 to 27 but the command expect 0 to 26 slot-- let GIVE = info[index].GIVE let itemName = GIVE let data = '' if (itemName.indexOf(',') !== -1) { let list = itemName.split(",") itemName = list[0] data = list[1] } let response = transmit(`/replaceitem entity @c slot.inventory ${slot} ${itemName} ${quantity} ${data}`, 1) //console.log(response) return true } } //creates the agent let agent = new AGENT() //exports the agent so it can be required elsewhere exports.agent = agent; ///////////////////////////////////////// /// Directions ////////////////////////// ///////////////////////////////////////// //this class makes it easier for the user to put in valid directions class Directions { constructor() { this.FORWARD = 'forward' this.BACK = 'back' this.LEFT = 'left' this.RIGHT = 'right' this.UP = 'up' this.DOWN = 'down' } } let d = new Directions() let directionList = [ 'forward', 'back', 'left', 'right', 'up', 'down' ] function validateDirection(direction) { if (directionList.includes(direction)) { return true } return false } ////////////////////////////////////////// //// SPECIAL FUNCTIONS /////////////////// ////////////////////////////////////////// class Special { constructor() { } about() { console.log(` createHeightMap()\nThis centers the player on a 256 x 256 world (old world). The function a heightmap of the world as an object containing and array of arrays of objects. It contains the heights, a colour code that matches different items in the database for the highest block in the world at that location in a 256 x 256 array of objects. `) } createHeightMap() { const fs = require('fs') //move player to center of world let step = 128 do { agent.tpToPlayer() let playerpos = agent.getPlayerPosition() agent.direct(`/tp @s ${playerpos.x - step} ${127} ${playerpos.z}`) console.log(`Stepping player east ${step}`) step = step / 2 } while (step >= 1) step = 128 do { agent.tpToPlayer() let playerpos = agent.getPlayerPosition() agent.direct(`/tp @s ${playerpos.x} ${127} ${playerpos.z - step}`) console.log(`Stepping player south ${step}`) step = step / 2 } while (step >= 1) console.log("Centering player") agent.tpToPlayer() let playerpos = agent.getPlayerPosition() agent.direct(`/tp @s ${playerpos.x + 128} ${127} ${playerpos.z + 128}`) agent.tpToPlayer() //spin around console.log("Visual 360 degree scan") playerpos = agent.getPlayerPosition() for (let y = 0; y < 360; y += 5) { agent.direct(`/tp @s ${playerpos.x} ${120} ${playerpos.z} facing ${playerpos.x + Math.sin(y / 360 * Math.PI) * 10} ${110} ${playerpos.z + Math.cos(y / 360 * Math.PI) * 10}`) } let maxX = -1000000 let minX = 1000000 let maxZ = -10000000 let minZ = 10000000 let chunkQuery = agent.direct("/getchunks overworld") let chunks = JSON.parse(chunkQuery.body.data) for (let i = 0; i < chunks.length; i++) { let coord = chunks[i] if (coord[0] > maxX) { maxX = coord[0] } if (coord[0] < minX) { minX = coord[0] } if (coord[1] > maxZ) { maxZ = coord[1] } if (coord[1] < minZ) { minZ = coord[1] } } console.log("Please wait. Getting ready to collect world heightmap. Please do not move the player until writing data is complete.") // console.log(minX, minZ) // console.log(maxX, maxZ) let pos = agent.getPlayerPosition() // console.log(pos) let avX = Math.round((minX + maxX) / 2) let avZ = Math.round((minZ + maxZ) / 2) minX = avX maxX = avX minZ = avZ maxZ = avZ let input = '';//"AAAAAQ*255" while (input !== "AAAAAQ*255") { let data = agent.direct(`/getchunkdata overworld ${minX} ${avZ} 127`, 1) input = data.body.data.replaceAll("\"", '') if (input !== "AAAAAQ*255") { minX-- } else { minX++ } } input = '';//"AAAAAQ*255" while (input !== "AAAAAQ*255") { let data = agent.direct(`/getchunkdata overworld ${maxX} ${avZ} 127`, 1) input = data.body.data.replaceAll("\"", '') if (input !== "AAAAAQ*255") { maxX++ } else { maxX-- } } input = '';//"AAAAAQ*255" while (input !== "AAAAAQ*255") { let data = agent.direct(`/getchunkdata overworld ${avX} ${minZ} 127`, 1) input = data.body.data.replaceAll("\"", '') if (input !== "AAAAAQ*255") { minZ-- } else { minZ++ } } input = '';//"AAAAAQ*255" while (input !== "AAAAAQ*255") { let data = agent.direct(`/getchunkdata overworld ${avX} ${maxZ} 127`, 1) input = data.body.data.replaceAll("\"", '') if (input !== "AAAAAQ*255") { maxZ++ } else { maxZ-- } } let map = [] for (let i = 0; i < 256; i++) { let lmap = [].fill(0) map.push(lmap) } let offsetX = 0 let offsetZ = 0 if (minX < 0) { offsetX = minX * -1 * 16 } if (minZ < 0) { offsetZ = minZ * -1 * 16 } if (minX > 0) { offsetX = -minX * 16 } if (maxZ > 0) { offsetZ = -minZ * 16 } let output = '' for (let x = minX; x <= maxX; x++) { for (let z = minZ; z <= maxZ; z++) { let data = agent.direct(`/getchunkdata overworld ${x} ${z} 127`, 1) let input = data.body.data.replaceAll("\"", '') if (input !== "AAAAAQ*255") { // console.log(`/getchunkdata overworld ${x} ${z} 127`) console.log(`Getting chunk at ${x} ${z}`) } else { console.log('error', x, z) } let line = input.split(',') //unzip stage let line2 = [] for (let i = 0; i < line.length; i++) { let code = line[i] if (code.indexOf('*') !== -1) { let values = code.split('*') for (let j = 0; j <= values[1]; j++) { line2.push(values[0]) } } else { line2.push(code) } } //un-dictionary stage for (let i = 0; i < line2.length; i++) { if (!isNaN(line2[i])) { line2[i] = line2[line2[i]] } } let px = x * 16 let pz = z * 16 //filter results to match search for (let i = 0; i < line2.length; i++) { let test = line2[i].substring(4, 6) const buffer = Buffer.from(test, 'base64'); const bufString = buffer.toString('hex'); let value = parseInt(bufString, 16) let dx = i % 16 let dz = Math.floor(i / 16) let posx = pz + dz + offsetZ let posz = px + dx + offsetX let posy = value let colorcode = line2[i].substring(0, 4) map[posx][posz] = { height: posy, colorcode } } } } let world = {} world.offsetX = -offsetZ world.offsetZ = -offsetX world.map = map console.log("Writing data to map.json") fs.writeFileSync(__dirname+"/map.json", JSON.stringify(world)) console.log("Done") return world; } } //////////////////////////////////////// /// ALL THE EXPORTS //////////////////// //////////////////////////////////////// exports.d = d exports.blocks = blocks exports.bn = blockNames exports.special = new Special()