mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-31 11:23:08 +02:00
Server/Minecraft data now agent-level
Also refactored mcdata as a class.
This commit is contained in:
parent
7a3a5b3f22
commit
418a2470de
15 changed files with 396 additions and 359 deletions
50
main.js
50
main.js
|
@ -2,8 +2,7 @@ import { AgentProcess } from './src/process/agent-process.js';
|
||||||
import settings from './settings.js';
|
import settings from './settings.js';
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import { serverInfo, findServers } from './src/utils/mcserver.js';
|
import { getServer } from './src/utils/mcserver.js';
|
||||||
import mc from 'minecraft-protocol';
|
|
||||||
|
|
||||||
function parseArguments() {
|
function parseArguments() {
|
||||||
return yargs(hideBin(process.argv))
|
return yargs(hideBin(process.argv))
|
||||||
|
@ -20,49 +19,6 @@ function getProfiles(args) {
|
||||||
return args.profiles || settings.profiles;
|
return args.profiles || settings.profiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getServer() {
|
|
||||||
let server = null;
|
|
||||||
let serverString = "";
|
|
||||||
let serverVersion = "";
|
|
||||||
|
|
||||||
// Search for server
|
|
||||||
if (settings.port == -1)
|
|
||||||
{
|
|
||||||
console.log("No port provided. Searching for LAN server...");
|
|
||||||
|
|
||||||
await findServers(settings.host, true).then((servers) => {
|
|
||||||
if (servers.length > 0)
|
|
||||||
server = servers[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new Error(`No server found on LAN.`);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
server = await serverInfo(settings.host, settings.port);
|
|
||||||
|
|
||||||
// Server not found
|
|
||||||
if (server == null)
|
|
||||||
throw new Error(`Server not found. (Host: ${settings.host}, Port: ${settings.port}) Check the host and port in settings.js.`);
|
|
||||||
|
|
||||||
serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`;
|
|
||||||
|
|
||||||
if (settings.minecraft_version === "auto")
|
|
||||||
serverVersion = server.version;
|
|
||||||
else
|
|
||||||
serverVersion = settings.minecraft_version;
|
|
||||||
|
|
||||||
// Server version unsupported / mismatch
|
|
||||||
if (mc.supportedVersions.indexOf(serverVersion) === -1)
|
|
||||||
throw new Error(`A server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`);
|
|
||||||
else if (settings.minecraft_version !== "auto" && server.version !== settings.minecraft_version)
|
|
||||||
throw new Error(`A server was found ${serverString}, but version is incorrect. Expected ${settings.minecraft_version}, but found ${server.version}.`);
|
|
||||||
else
|
|
||||||
console.log(`Server found. ${serverString}`);
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const args = parseArguments();
|
const args = parseArguments();
|
||||||
const profiles = getProfiles(args);
|
const profiles = getProfiles(args);
|
||||||
|
@ -71,10 +27,10 @@ async function main() {
|
||||||
|
|
||||||
// Get server
|
// Get server
|
||||||
const server = await getServer();
|
const server = await getServer();
|
||||||
|
|
||||||
for (let i=0; i<profiles.length; i++) {
|
for (let i=0; i<profiles.length; i++) {
|
||||||
const agent = new AgentProcess();
|
const agent = new AgentProcess();
|
||||||
agent.start(profiles[i], load_memory, init_message, i);
|
agent.start(profiles[i], load_memory, init_message, server.host, server.port, server.version, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { History } from './history.js';
|
||||||
import { Coder } from './coder.js';
|
import { Coder } from './coder.js';
|
||||||
import { Prompter } from './prompter.js';
|
import { Prompter } from './prompter.js';
|
||||||
import { initModes } from './modes.js';
|
import { initModes } from './modes.js';
|
||||||
import { initBot } from '../utils/mcdata.js';
|
import { mc } from '../utils/mcdata.js';
|
||||||
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
||||||
import { ActionManager } from './action_manager.js';
|
import { ActionManager } from './action_manager.js';
|
||||||
import { NPCContoller } from './npc/controller.js';
|
import { NPCContoller } from './npc/controller.js';
|
||||||
|
@ -13,7 +13,7 @@ import { addViewer } from './viewer.js';
|
||||||
import settings from '../../settings.js';
|
import settings from '../../settings.js';
|
||||||
|
|
||||||
export class Agent {
|
export class Agent {
|
||||||
async start(profile_fp, load_mem=false, init_message=null, count_id=0) {
|
async start(profile_fp, load_mem=false, init_message=null, server_host=null, server_port=0, version=null, count_id=0) {
|
||||||
try {
|
try {
|
||||||
// Add validation for profile_fp
|
// Add validation for profile_fp
|
||||||
if (!profile_fp) {
|
if (!profile_fp) {
|
||||||
|
@ -50,9 +50,16 @@ export class Agent {
|
||||||
throw new Error(`Failed to initialize examples: ${error.message || error}`);
|
throw new Error(`Failed to initialize examples: ${error.message || error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Initializing Minecraft data...");
|
||||||
|
try {
|
||||||
|
mc.init(server_host, server_port, version);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to initialize Minecraft data: ${error.message || error}`);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Logging into minecraft...');
|
console.log('Logging into minecraft...');
|
||||||
try {
|
try {
|
||||||
this.bot = initBot(this.name);
|
this.bot = mc.initBot(this.name);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to initialize Minecraft bot: ${error.message || error}`);
|
throw new Error(`Failed to initialize Minecraft bot: ${error.message || error}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { getBlockId, getItemId } from "../../utils/mcdata.js";
|
import { mc } from "../../utils/mcdata.js";
|
||||||
import { actionsList } from './actions.js';
|
import { actionsList } from './actions.js';
|
||||||
import { queryList } from './queries.js';
|
import { queryList } from './queries.js';
|
||||||
|
|
||||||
|
@ -154,9 +154,9 @@ function parseCommandMessage(message) {
|
||||||
suppressNoDomainWarning = true; //Don't spam console. Only give the warning once.
|
suppressNoDomainWarning = true; //Don't spam console. Only give the warning once.
|
||||||
}
|
}
|
||||||
} else if(param.type === 'BlockName') { //Check that there is a block with this name
|
} else if(param.type === 'BlockName') { //Check that there is a block with this name
|
||||||
if(getBlockId(arg) == null) return `Invalid block type: ${arg}.`
|
if(mc.getBlockId(arg) == null) return `Invalid block type: ${arg}.`
|
||||||
} else if(param.type === 'ItemName') { //Check that there is an item with this name
|
} else if(param.type === 'ItemName') { //Check that there is an item with this name
|
||||||
if(getItemId(arg) == null) return `Invalid item type: ${arg}.`
|
if(mc.getItemId(arg) == null) return `Invalid item type: ${arg}.`
|
||||||
}
|
}
|
||||||
args[i] = arg;
|
args[i] = arg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as world from '../library/world.js';
|
import * as world from '../library/world.js';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
|
|
||||||
|
|
||||||
const pad = (str) => {
|
const pad = (str) => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as mc from "../../utils/mcdata.js";
|
import { mc } from '../../utils/mcdata.js';
|
||||||
import * as world from "./world.js";
|
import * as world from "./world.js";
|
||||||
import pf from 'mineflayer-pathfinder';
|
import pf from 'mineflayer-pathfinder';
|
||||||
import Vec3 from 'vec3';
|
import Vec3 from 'vec3';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import pf from 'mineflayer-pathfinder';
|
import pf from 'mineflayer-pathfinder';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
|
|
||||||
|
|
||||||
export function getNearestFreeSpace(bot, size=1, distance=8) {
|
export function getNearestFreeSpace(bot, size=1, distance=8) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as skills from './library/skills.js';
|
import * as skills from './library/skills.js';
|
||||||
import * as world from './library/world.js';
|
import * as world from './library/world.js';
|
||||||
import * as mc from '../utils/mcdata.js';
|
import { mc } from '../utils/mcdata.js';
|
||||||
import settings from '../../settings.js'
|
import settings from '../../settings.js'
|
||||||
import { handleTranslation } from '../utils/translator.js';
|
import { handleTranslation } from '../utils/translator.js';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Vec3 } from 'vec3';
|
import { Vec3 } from 'vec3';
|
||||||
import * as skills from '../library/skills.js';
|
import * as skills from '../library/skills.js';
|
||||||
import * as world from '../library/world.js';
|
import * as world from '../library/world.js';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
import { blockSatisfied, getTypeOfGeneric, rotateXZ } from './utils.js';
|
import { blockSatisfied, getTypeOfGeneric, rotateXZ } from './utils.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { BuildGoal } from './build_goal.js';
|
||||||
import { itemSatisfied, rotateXZ } from './utils.js';
|
import { itemSatisfied, rotateXZ } from './utils.js';
|
||||||
import * as skills from '../library/skills.js';
|
import * as skills from '../library/skills.js';
|
||||||
import * as world from '../library/world.js';
|
import * as world from '../library/world.js';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
|
|
||||||
|
|
||||||
export class NPCContoller {
|
export class NPCContoller {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as skills from '../library/skills.js';
|
import * as skills from '../library/skills.js';
|
||||||
import * as world from '../library/world.js';
|
import * as world from '../library/world.js';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
import { itemSatisfied } from './utils.js';
|
import { itemSatisfied } from './utils.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as world from '../library/world.js';
|
import * as world from '../library/world.js';
|
||||||
import * as mc from '../../utils/mcdata.js';
|
import { mc } from '../../utils/mcdata.js';
|
||||||
|
|
||||||
|
|
||||||
export function getTypeOfGeneric(bot, block_name) {
|
export function getTypeOfGeneric(bot, block_name) {
|
||||||
// Get type of wooden block
|
// Get type of wooden block
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { spawn } from 'child_process';
|
||||||
export class AgentProcess {
|
export class AgentProcess {
|
||||||
static runningCount = 0;
|
static runningCount = 0;
|
||||||
|
|
||||||
start(profile, load_memory=false, init_message=null, count_id=0) {
|
start(profile, load_memory=false, init_message=null, server_host=null, server_port=0, server_version=null, count_id=0) {
|
||||||
let args = ['src/process/init-agent.js', this.name];
|
let args = ['src/process/init-agent.js', this.name];
|
||||||
args.push('-p', profile);
|
args.push('-p', profile);
|
||||||
args.push('-c', count_id);
|
args.push('-c', count_id);
|
||||||
|
@ -12,6 +12,11 @@ export class AgentProcess {
|
||||||
if (init_message)
|
if (init_message)
|
||||||
args.push('-m', init_message);
|
args.push('-m', init_message);
|
||||||
|
|
||||||
|
// Pass server/version info to agent
|
||||||
|
args.push('--server_host', server_host);
|
||||||
|
args.push('--server_port', server_port);
|
||||||
|
args.push('--server_version', server_version);
|
||||||
|
|
||||||
const agentProcess = spawn('node', args, {
|
const agentProcess = spawn('node', args, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
stderr: 'inherit',
|
stderr: 'inherit',
|
||||||
|
@ -34,7 +39,7 @@ export class AgentProcess {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Restarting agent...');
|
console.log('Restarting agent...');
|
||||||
this.start(profile, true, 'Agent process restarted.', count_id);
|
this.start(profile, true, 'Agent process restarted.', server_host, server_port, server_version, count_id);
|
||||||
last_restart = Date.now();
|
last_restart = Date.now();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,6 +38,18 @@ const argv = yargs(args)
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: 0,
|
default: 0,
|
||||||
description: 'identifying count for multi-agent scenarios',
|
description: 'identifying count for multi-agent scenarios',
|
||||||
|
})
|
||||||
|
.option('server_host', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'minecraft server host',
|
||||||
|
})
|
||||||
|
.option('server_port', {
|
||||||
|
type: 'number',
|
||||||
|
description: 'minecraft server port',
|
||||||
|
})
|
||||||
|
.option('server_version', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'minecraft version'
|
||||||
}).argv;
|
}).argv;
|
||||||
|
|
||||||
// Wrap agent start in async IIFE with proper error handling
|
// Wrap agent start in async IIFE with proper error handling
|
||||||
|
@ -45,7 +57,7 @@ const argv = yargs(args)
|
||||||
try {
|
try {
|
||||||
console.log('Starting agent with profile:', argv.profile);
|
console.log('Starting agent with profile:', argv.profile);
|
||||||
const agent = new Agent();
|
const agent = new Agent();
|
||||||
await agent.start(argv.profile, argv.load_memory, argv.init_message, argv.count_id);
|
await agent.start(argv.profile, argv.load_memory, argv.init_message, argv.server_host, argv.server_port, argv.server_version, argv.count_id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to start agent process:', {
|
console.error('Failed to start agent process:', {
|
||||||
message: error.message || 'No error message',
|
message: error.message || 'No error message',
|
||||||
|
|
|
@ -9,317 +9,331 @@ import { plugin as autoEat } from 'mineflayer-auto-eat';
|
||||||
import plugin from 'mineflayer-armor-manager';
|
import plugin from 'mineflayer-armor-manager';
|
||||||
const armorManager = plugin;
|
const armorManager = plugin;
|
||||||
|
|
||||||
const mc_version = settings.minecraft_version;
|
class MinecraftData {
|
||||||
const mcdata = minecraftData(mc_version);
|
constructor() {
|
||||||
const Item = prismarine_items(mc_version);
|
this.mcdata = null;
|
||||||
|
this.Item = null;
|
||||||
|
|
||||||
/**
|
this.server_version = null;
|
||||||
* @typedef {string} ItemName
|
this.server_port = null;
|
||||||
* @typedef {string} BlockName
|
this.server_host = null;
|
||||||
*/
|
|
||||||
|
|
||||||
export const WOOD_TYPES = ['oak', 'spruce', 'birch', 'jungle', 'acacia', 'dark_oak'];
|
|
||||||
export const MATCHING_WOOD_BLOCKS = [
|
|
||||||
'log',
|
|
||||||
'planks',
|
|
||||||
'sign',
|
|
||||||
'boat',
|
|
||||||
'fence_gate',
|
|
||||||
'door',
|
|
||||||
'fence',
|
|
||||||
'slab',
|
|
||||||
'stairs',
|
|
||||||
'button',
|
|
||||||
'pressure_plate',
|
|
||||||
'trapdoor'
|
|
||||||
]
|
|
||||||
export const WOOL_COLORS = [
|
|
||||||
'white',
|
|
||||||
'orange',
|
|
||||||
'magenta',
|
|
||||||
'light_blue',
|
|
||||||
'yellow',
|
|
||||||
'lime',
|
|
||||||
'pink',
|
|
||||||
'gray',
|
|
||||||
'light_gray',
|
|
||||||
'cyan',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'brown',
|
|
||||||
'green',
|
|
||||||
'red',
|
|
||||||
'black'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
export function initBot(username) {
|
|
||||||
let bot = createBot({
|
|
||||||
username: username,
|
|
||||||
|
|
||||||
host: settings.host,
|
|
||||||
port: settings.port,
|
|
||||||
auth: settings.auth,
|
|
||||||
|
|
||||||
version: mc_version,
|
|
||||||
});
|
|
||||||
bot.loadPlugin(pathfinder);
|
|
||||||
bot.loadPlugin(pvp);
|
|
||||||
bot.loadPlugin(collectblock);
|
|
||||||
bot.loadPlugin(autoEat);
|
|
||||||
bot.loadPlugin(armorManager); // auto equip armor
|
|
||||||
|
|
||||||
return bot;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isHuntable(mob) {
|
|
||||||
if (!mob || !mob.name) return false;
|
|
||||||
const animals = ['chicken', 'cow', 'llama', 'mooshroom', 'pig', 'rabbit', 'sheep'];
|
|
||||||
return animals.includes(mob.name.toLowerCase()) && !mob.metadata[16]; // metadata 16 is not baby
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isHostile(mob) {
|
|
||||||
if (!mob || !mob.name) return false;
|
|
||||||
return (mob.type === 'mob' || mob.type === 'hostile') && mob.name !== 'iron_golem' && mob.name !== 'snow_golem';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemId(itemName) {
|
|
||||||
let item = mcdata.itemsByName[itemName];
|
|
||||||
if (item) {
|
|
||||||
return item.id;
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemName(itemId) {
|
init(host, port, version) {
|
||||||
let item = mcdata.items[itemId]
|
this.server_version = version;
|
||||||
if (item) {
|
this.server_port = port;
|
||||||
return item.name;
|
this.server_host = host;
|
||||||
|
|
||||||
|
this.mcdata = minecraftData(this.server_version);
|
||||||
|
this.Item = prismarine_items(this.server_version);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBlockId(blockName) {
|
initBot(username) {
|
||||||
let block = mcdata.blocksByName[blockName];
|
let bot = createBot({
|
||||||
if (block) {
|
username: username,
|
||||||
return block.id;
|
host: this.server_host,
|
||||||
|
port: this.server_port,
|
||||||
|
auth: settings.auth,
|
||||||
|
version: this.server_version,
|
||||||
|
});
|
||||||
|
bot.loadPlugin(pathfinder);
|
||||||
|
bot.loadPlugin(pvp);
|
||||||
|
bot.loadPlugin(collectblock);
|
||||||
|
bot.loadPlugin(autoEat);
|
||||||
|
bot.loadPlugin(armorManager); // auto equip armor
|
||||||
|
return bot;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBlockName(blockId) {
|
isHuntable(mob) {
|
||||||
let block = mcdata.blocks[blockId]
|
if (!mob || !mob.name) return false;
|
||||||
if (block) {
|
const animals = ['chicken', 'cow', 'llama', 'mooshroom', 'pig', 'rabbit', 'sheep'];
|
||||||
return block.name;
|
return animals.includes(mob.name.toLowerCase()) && !mob.metadata[16]; // metadata 16 is not baby
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllItems(ignore) {
|
isHostile(mob) {
|
||||||
if (!ignore) {
|
if (!mob || !mob.name) return false;
|
||||||
ignore = [];
|
return (mob.type === 'mob' || mob.type === 'hostile') && mob.name !== 'iron_golem' && mob.name !== 'snow_golem';
|
||||||
}
|
}
|
||||||
let items = []
|
|
||||||
for (const itemId in mcdata.items) {
|
getItemId(itemName) {
|
||||||
const item = mcdata.items[itemId];
|
let item = this.mcdata.itemsByName[itemName];
|
||||||
if (!ignore.includes(item.name)) {
|
if (item) {
|
||||||
items.push(item);
|
return item.id;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllItemIds(ignore) {
|
|
||||||
const items = getAllItems(ignore);
|
|
||||||
let itemIds = [];
|
|
||||||
for (const item of items) {
|
|
||||||
itemIds.push(item.id);
|
|
||||||
}
|
|
||||||
return itemIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllBlocks(ignore) {
|
|
||||||
if (!ignore) {
|
|
||||||
ignore = [];
|
|
||||||
}
|
|
||||||
let blocks = []
|
|
||||||
for (const blockId in mcdata.blocks) {
|
|
||||||
const block = mcdata.blocks[blockId];
|
|
||||||
if (!ignore.includes(block.name)) {
|
|
||||||
blocks.push(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllBlockIds(ignore) {
|
|
||||||
const blocks = getAllBlocks(ignore);
|
|
||||||
let blockIds = [];
|
|
||||||
for (const block of blocks) {
|
|
||||||
blockIds.push(block.id);
|
|
||||||
}
|
|
||||||
return blockIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllBiomes() {
|
|
||||||
return mcdata.biomes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemCraftingRecipes(itemName) {
|
|
||||||
let itemId = getItemId(itemName);
|
|
||||||
if (!mcdata.recipes[itemId]) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let recipes = [];
|
getItemName(itemId) {
|
||||||
for (let r of mcdata.recipes[itemId]) {
|
let item = this.mcdata.items[itemId]
|
||||||
let recipe = {};
|
if (item) {
|
||||||
let ingredients = [];
|
return item.name;
|
||||||
if (r.ingredients) {
|
|
||||||
ingredients = r.ingredients;
|
|
||||||
} else if (r.inShape) {
|
|
||||||
ingredients = r.inShape.flat();
|
|
||||||
}
|
}
|
||||||
for (let ingredient of ingredients) {
|
|
||||||
let ingredientName = getItemName(ingredient);
|
|
||||||
if (ingredientName === null) continue;
|
|
||||||
if (!recipe[ingredientName])
|
|
||||||
recipe[ingredientName] = 0;
|
|
||||||
recipe[ingredientName]++;
|
|
||||||
}
|
|
||||||
recipes.push(recipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
return recipes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSmeltable(itemName) {
|
|
||||||
const misc_smeltables = ['beef', 'chicken', 'cod', 'mutton', 'porkchop', 'rabbit', 'salmon', 'tropical_fish', 'potato', 'kelp', 'sand', 'cobblestone', 'clay_ball'];
|
|
||||||
return itemName.includes('raw') || itemName.includes('log') || misc_smeltables.includes(itemName);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSmeltingFuel(bot) {
|
|
||||||
let fuel = bot.inventory.items().find(i => i.name === 'coal' || i.name === 'charcoal')
|
|
||||||
if (fuel)
|
|
||||||
return fuel;
|
|
||||||
fuel = bot.inventory.items().find(i => i.name.includes('log') || i.name.includes('planks'))
|
|
||||||
if (fuel)
|
|
||||||
return fuel;
|
|
||||||
return bot.inventory.items().find(i => i.name === 'coal_block' || i.name === 'lava_bucket');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFuelSmeltOutput(fuelName) {
|
|
||||||
if (fuelName === 'coal' || fuelName === 'charcoal')
|
|
||||||
return 8;
|
|
||||||
if (fuelName.includes('log') || fuelName.includes('planks'))
|
|
||||||
return 1.5
|
|
||||||
if (fuelName === 'coal_block')
|
|
||||||
return 80;
|
|
||||||
if (fuelName === 'lava_bucket')
|
|
||||||
return 100;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemSmeltingIngredient(itemName) {
|
|
||||||
return {
|
|
||||||
baked_potato: 'potato',
|
|
||||||
steak: 'raw_beef',
|
|
||||||
cooked_chicken: 'raw_chicken',
|
|
||||||
cooked_cod: 'raw_cod',
|
|
||||||
cooked_mutton: 'raw_mutton',
|
|
||||||
cooked_porkchop: 'raw_porkchop',
|
|
||||||
cooked_rabbit: 'raw_rabbit',
|
|
||||||
cooked_salmon: 'raw_salmon',
|
|
||||||
dried_kelp: 'kelp',
|
|
||||||
iron_ingot: 'raw_iron',
|
|
||||||
gold_ingot: 'raw_gold',
|
|
||||||
copper_ingot: 'raw_copper',
|
|
||||||
glass: 'sand'
|
|
||||||
}[itemName];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemBlockSources(itemName) {
|
|
||||||
let itemId = getItemId(itemName);
|
|
||||||
let sources = [];
|
|
||||||
for (let block of getAllBlocks()) {
|
|
||||||
if (block.drops.includes(itemId)) {
|
|
||||||
sources.push(block.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sources;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemAnimalSource(itemName) {
|
|
||||||
return {
|
|
||||||
raw_beef: 'cow',
|
|
||||||
raw_chicken: 'chicken',
|
|
||||||
raw_cod: 'cod',
|
|
||||||
raw_mutton: 'sheep',
|
|
||||||
raw_porkchop: 'pig',
|
|
||||||
raw_rabbit: 'rabbit',
|
|
||||||
raw_salmon: 'salmon',
|
|
||||||
leather: 'cow',
|
|
||||||
wool: 'sheep'
|
|
||||||
}[itemName];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBlockTool(blockName) {
|
|
||||||
let block = mcdata.blocksByName[blockName];
|
|
||||||
if (!block || !block.harvestTools) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return getItemName(Object.keys(block.harvestTools)[0]); // Double check first tool is always simplest
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeItem(name, amount=1) {
|
getBlockId(blockName) {
|
||||||
return new Item(getItemId(name), amount);
|
let block = this.mcdata.blocksByName[blockName];
|
||||||
}
|
if (block) {
|
||||||
|
return block.id;
|
||||||
/**
|
|
||||||
* Returns the number of ingredients required to use the recipe once.
|
|
||||||
*
|
|
||||||
* @param {Recipe} recipe
|
|
||||||
* @returns {Object<mc.ItemName, number>} an object describing the number of each ingredient.
|
|
||||||
*/
|
|
||||||
export function ingredientsFromPrismarineRecipe(recipe) {
|
|
||||||
let requiredIngedients = {};
|
|
||||||
if (recipe.inShape)
|
|
||||||
for (const ingredient of recipe.inShape.flat()) {
|
|
||||||
if(ingredient.id<0) continue; //prismarine-recipe uses id -1 as an empty crafting slot
|
|
||||||
const ingredientName = getItemName(ingredient.id);
|
|
||||||
requiredIngedients[ingredientName] ??=0;
|
|
||||||
requiredIngedients[ingredientName] += ingredient.count;
|
|
||||||
}
|
|
||||||
if (recipe.ingredients)
|
|
||||||
for (const ingredient of recipe.ingredients) {
|
|
||||||
if(ingredient.id<0) continue;
|
|
||||||
const ingredientName = getItemName(ingredient.id);
|
|
||||||
requiredIngedients[ingredientName] ??=0;
|
|
||||||
requiredIngedients[ingredientName] -= ingredient.count;
|
|
||||||
//Yes, the `-=` is intended.
|
|
||||||
//prismarine-recipe uses positive numbers for the shaped ingredients but negative for unshaped.
|
|
||||||
//Why this is the case is beyond my understanding.
|
|
||||||
}
|
|
||||||
return requiredIngedients;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of times an action, such as a crafing recipe, can be completed before running out of resources.
|
|
||||||
* @template T - doesn't have to be an item. This could be any resource.
|
|
||||||
* @param {Object.<T, number>} availableItems - The resources available; e.g, `{'cobble_stone': 7, 'stick': 10}`
|
|
||||||
* @param {Object.<T, number>} requiredItems - The resources required to complete the action once; e.g, `{'cobble_stone': 3, 'stick': 2}`
|
|
||||||
* @param {boolean} discrete - Is the action discrete?
|
|
||||||
* @returns {{num: number, limitingResource: (T | null)}} the number of times the action can be completed and the limmiting resource; e.g `{num: 2, limitingResource: 'cobble_stone'}`
|
|
||||||
*/
|
|
||||||
export function calculateLimitingResource(availableItems, requiredItems, discrete=true) {
|
|
||||||
let limitingResource = null;
|
|
||||||
let num = Infinity;
|
|
||||||
for (const itemType in requiredItems) {
|
|
||||||
if (availableItems[itemType] < requiredItems[itemType] * num) {
|
|
||||||
limitingResource = itemType;
|
|
||||||
num = availableItems[itemType] / requiredItems[itemType];
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if(discrete) num = Math.floor(num);
|
|
||||||
return {num, limitingResource}
|
getBlockName(blockId) {
|
||||||
}
|
let block = this.mcdata.blocks[blockId]
|
||||||
|
if (block) {
|
||||||
|
return block.name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllItems(ignore) {
|
||||||
|
if (!ignore) {
|
||||||
|
ignore = [];
|
||||||
|
}
|
||||||
|
let items = []
|
||||||
|
for (const itemId in this.mcdata.items) {
|
||||||
|
const item = this.mcdata.items[itemId];
|
||||||
|
if (!ignore.includes(item.name)) {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllItemIds(ignore) {
|
||||||
|
const items = this.getAllItems(ignore);
|
||||||
|
let itemIds = [];
|
||||||
|
for (const item of items) {
|
||||||
|
itemIds.push(item.id);
|
||||||
|
}
|
||||||
|
return itemIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllBlocks(ignore) {
|
||||||
|
if (!ignore) {
|
||||||
|
ignore = [];
|
||||||
|
}
|
||||||
|
let blocks = []
|
||||||
|
for (const blockId in this.mcdata.blocks) {
|
||||||
|
const block = this.mcdata.blocks[blockId];
|
||||||
|
if (!ignore.includes(block.name)) {
|
||||||
|
blocks.push(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllBlockIds(ignore) {
|
||||||
|
const blocks = this.getAllBlocks(ignore);
|
||||||
|
let blockIds = [];
|
||||||
|
for (const block of blocks) {
|
||||||
|
blockIds.push(block.id);
|
||||||
|
}
|
||||||
|
return blockIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllBiomes() {
|
||||||
|
return this.mcdata.biomes;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemCraftingRecipes(itemName) {
|
||||||
|
let itemId = this.getItemId(itemName);
|
||||||
|
if (!this.mcdata.recipes[itemId]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let recipes = [];
|
||||||
|
for (let r of this.mcdata.recipes[itemId]) {
|
||||||
|
let recipe = {};
|
||||||
|
let ingredients = [];
|
||||||
|
if (r.ingredients) {
|
||||||
|
ingredients = r.ingredients;
|
||||||
|
} else if (r.inShape) {
|
||||||
|
ingredients = r.inShape.flat();
|
||||||
|
}
|
||||||
|
for (let ingredient of ingredients) {
|
||||||
|
let ingredientName = this.getItemName(ingredient);
|
||||||
|
if (ingredientName === null) continue;
|
||||||
|
if (!recipe[ingredientName])
|
||||||
|
recipe[ingredientName] = 0;
|
||||||
|
recipe[ingredientName]++;
|
||||||
|
}
|
||||||
|
recipes.push(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSmeltable(itemName) {
|
||||||
|
const misc_smeltables = ['beef', 'chicken', 'cod', 'mutton', 'porkchop', 'rabbit', 'salmon', 'tropical_fish', 'potato', 'kelp', 'sand', 'cobblestone', 'clay_ball'];
|
||||||
|
return itemName.includes('raw') || itemName.includes('log') || misc_smeltables.includes(itemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSmeltingFuel(bot) {
|
||||||
|
let fuel = bot.inventory.items().find(i => i.name === 'coal' || i.name === 'charcoal')
|
||||||
|
if (fuel)
|
||||||
|
return fuel;
|
||||||
|
fuel = bot.inventory.items().find(i => i.name.includes('log') || i.name.includes('planks'))
|
||||||
|
if (fuel)
|
||||||
|
return fuel;
|
||||||
|
return bot.inventory.items().find(i => i.name === 'coal_block' || i.name === 'lava_bucket');
|
||||||
|
}
|
||||||
|
|
||||||
|
getFuelSmeltOutput(fuelName) {
|
||||||
|
if (fuelName === 'coal' || fuelName === 'charcoal')
|
||||||
|
return 8;
|
||||||
|
if (fuelName.includes('log') || fuelName.includes('planks'))
|
||||||
|
return 1.5
|
||||||
|
if (fuelName === 'coal_block')
|
||||||
|
return 80;
|
||||||
|
if (fuelName === 'lava_bucket')
|
||||||
|
return 100;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemSmeltingIngredient(itemName) {
|
||||||
|
return {
|
||||||
|
baked_potato: 'potato',
|
||||||
|
steak: 'raw_beef',
|
||||||
|
cooked_chicken: 'raw_chicken',
|
||||||
|
cooked_cod: 'raw_cod',
|
||||||
|
cooked_mutton: 'raw_mutton',
|
||||||
|
cooked_porkchop: 'raw_porkchop',
|
||||||
|
cooked_rabbit: 'raw_rabbit',
|
||||||
|
cooked_salmon: 'raw_salmon',
|
||||||
|
dried_kelp: 'kelp',
|
||||||
|
iron_ingot: 'raw_iron',
|
||||||
|
gold_ingot: 'raw_gold',
|
||||||
|
copper_ingot: 'raw_copper',
|
||||||
|
glass: 'sand'
|
||||||
|
}[itemName];
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemBlockSources(itemName) {
|
||||||
|
let itemId = this.getItemId(itemName);
|
||||||
|
let sources = [];
|
||||||
|
for (let block of this.getAllBlocks()) {
|
||||||
|
if (block.drops.includes(itemId)) {
|
||||||
|
sources.push(block.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemAnimalSource(itemName) {
|
||||||
|
return {
|
||||||
|
raw_beef: 'cow',
|
||||||
|
raw_chicken: 'chicken',
|
||||||
|
raw_cod: 'cod',
|
||||||
|
raw_mutton: 'sheep',
|
||||||
|
raw_porkchop: 'pig',
|
||||||
|
raw_rabbit: 'rabbit',
|
||||||
|
raw_salmon: 'salmon',
|
||||||
|
leather: 'cow',
|
||||||
|
wool: 'sheep'
|
||||||
|
}[itemName];
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockTool(blockName) {
|
||||||
|
let block = this.mcdata.blocksByName[blockName];
|
||||||
|
if (!block || !block.harvestTools) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.getItemName(Object.keys(block.harvestTools)[0]); // Double check first tool is always simplest
|
||||||
|
}
|
||||||
|
|
||||||
|
makeItem(name, amount=1) {
|
||||||
|
return new this.Item(this.getItemId(name), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of ingredients required to use the recipe once.
|
||||||
|
*
|
||||||
|
* @param {Recipe} recipe
|
||||||
|
* @returns {Object<mc.ItemName, number>} an object describing the number of each ingredient.
|
||||||
|
*/
|
||||||
|
ingredientsFromPrismarineRecipe(recipe) {
|
||||||
|
let requiredIngedients = {};
|
||||||
|
if (recipe.inShape)
|
||||||
|
for (const ingredient of recipe.inShape.flat()) {
|
||||||
|
if(ingredient.id<0) continue; //prismarine-recipe uses id -1 as an empty crafting slot
|
||||||
|
const ingredientName = this.getItemName(ingredient.id);
|
||||||
|
requiredIngedients[ingredientName] ??=0;
|
||||||
|
requiredIngedients[ingredientName] += ingredient.count;
|
||||||
|
}
|
||||||
|
if (recipe.ingredients)
|
||||||
|
for (const ingredient of recipe.ingredients) {
|
||||||
|
if(ingredient.id<0) continue;
|
||||||
|
const ingredientName = this.getItemName(ingredient.id);
|
||||||
|
requiredIngedients[ingredientName] ??=0;
|
||||||
|
requiredIngedients[ingredientName] -= ingredient.count;
|
||||||
|
//Yes, the `-=` is intended.
|
||||||
|
//prismarine-recipe uses positive numbers for the shaped ingredients but negative for unshaped.
|
||||||
|
//Why this is the case is beyond my understanding.
|
||||||
|
}
|
||||||
|
return requiredIngedients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the number of times an action, such as a crafing recipe, can be completed before running out of resources.
|
||||||
|
* @template T - doesn't have to be an item. This could be any resource.
|
||||||
|
* @param {Object.<T, number>} availableItems - The resources available; e.g, `{'cobble_stone': 7, 'stick': 10}`
|
||||||
|
* @param {Object.<T, number>} requiredItems - The resources required to complete the action once; e.g, `{'cobble_stone': 3, 'stick': 2}`
|
||||||
|
* @param {boolean} discrete - Is the action discrete?
|
||||||
|
* @returns {{num: number, limitingResource: (T | null)}} the number of times the action can be completed and the limmiting resource; e.g `{num: 2, limitingResource: 'cobble_stone'}`
|
||||||
|
*/
|
||||||
|
calculateLimitingResource(availableItems, requiredItems, discrete=true) {
|
||||||
|
let limitingResource = null;
|
||||||
|
let num = Infinity;
|
||||||
|
for (const itemType in requiredItems) {
|
||||||
|
if (availableItems[itemType] < requiredItems[itemType] * num) {
|
||||||
|
limitingResource = itemType;
|
||||||
|
num = availableItems[itemType] / requiredItems[itemType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(discrete) num = Math.floor(num);
|
||||||
|
return {num, limitingResource}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {string} ItemName
|
||||||
|
* @typedef {string} BlockName
|
||||||
|
*/
|
||||||
|
|
||||||
|
WOOD_TYPES = ['oak', 'spruce', 'birch', 'jungle', 'acacia', 'dark_oak'];
|
||||||
|
MATCHING_WOOD_BLOCKS = [
|
||||||
|
'log',
|
||||||
|
'planks',
|
||||||
|
'sign',
|
||||||
|
'boat',
|
||||||
|
'fence_gate',
|
||||||
|
'door',
|
||||||
|
'fence',
|
||||||
|
'slab',
|
||||||
|
'stairs',
|
||||||
|
'button',
|
||||||
|
'pressure_plate',
|
||||||
|
'trapdoor'
|
||||||
|
]
|
||||||
|
WOOL_COLORS = [
|
||||||
|
'white',
|
||||||
|
'orange',
|
||||||
|
'magenta',
|
||||||
|
'light_blue',
|
||||||
|
'yellow',
|
||||||
|
'lime',
|
||||||
|
'pink',
|
||||||
|
'gray',
|
||||||
|
'light_gray',
|
||||||
|
'cyan',
|
||||||
|
'purple',
|
||||||
|
'blue',
|
||||||
|
'brown',
|
||||||
|
'green',
|
||||||
|
'red',
|
||||||
|
'black'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mc = new MinecraftData();
|
|
@ -1,3 +1,4 @@
|
||||||
|
import settings from '../../settings.js';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import mc from 'minecraft-protocol';
|
import mc from 'minecraft-protocol';
|
||||||
|
|
||||||
|
@ -81,4 +82,47 @@ export async function findServers(ip, earlyExit = false, timeout = 100) {
|
||||||
console.log = originalConsoleLog;
|
console.log = originalConsoleLog;
|
||||||
|
|
||||||
return servers;
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getServer() {
|
||||||
|
let server = null;
|
||||||
|
let serverString = "";
|
||||||
|
let serverVersion = "";
|
||||||
|
|
||||||
|
// Search for server
|
||||||
|
if (settings.port == -1)
|
||||||
|
{
|
||||||
|
console.log(`No port provided. Searching for LAN server on host ${settings.host}...`);
|
||||||
|
|
||||||
|
await findServers(settings.host, true).then((servers) => {
|
||||||
|
if (servers.length > 0)
|
||||||
|
server = servers[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (server == null)
|
||||||
|
throw new Error(`No server found on LAN.`);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
server = await serverInfo(settings.host, settings.port);
|
||||||
|
|
||||||
|
// Server not found
|
||||||
|
if (server == null)
|
||||||
|
throw new Error(`Server not found. (Host: ${settings.host}, Port: ${settings.port}) Check the host and port in settings.js.`);
|
||||||
|
|
||||||
|
serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`;
|
||||||
|
|
||||||
|
if (settings.minecraft_version === "auto")
|
||||||
|
serverVersion = server.version;
|
||||||
|
else
|
||||||
|
serverVersion = settings.minecraft_version;
|
||||||
|
|
||||||
|
// Server version unsupported / mismatch
|
||||||
|
if (mc.supportedVersions.indexOf(serverVersion) === -1)
|
||||||
|
throw new Error(`A server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`);
|
||||||
|
else if (settings.minecraft_version !== "auto" && server.version !== settings.minecraft_version)
|
||||||
|
throw new Error(`A server was found ${serverString}, but version is incorrect. Expected ${settings.minecraft_version}, but found ${server.version}.`);
|
||||||
|
else
|
||||||
|
console.log(`Server found. ${serverString}`);
|
||||||
|
|
||||||
|
return server;
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue