Server/Minecraft data now agent-level

Also refactored mcdata as a class.
This commit is contained in:
Nimi 2024-11-14 15:00:53 -06:00
parent 7a3a5b3f22
commit 418a2470de
15 changed files with 396 additions and 359 deletions

48
main.js
View file

@ -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);
@ -74,7 +30,7 @@ async function main() {
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);
} }
} }

View file

@ -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}`);
} }

View file

@ -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;
} }

View file

@ -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) => {

View file

@ -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';

View file

@ -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) {

View file

@ -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';

View file

@ -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';

View file

@ -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 {

View file

@ -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';

View file

@ -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

View file

@ -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();
} }
}); });

View file

@ -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',

View file

@ -9,119 +9,91 @@ 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']; init(host, port, version) {
export const MATCHING_WOOD_BLOCKS = [ this.server_version = version;
'log', this.server_port = port;
'planks', this.server_host = host;
'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'
]
this.mcdata = minecraftData(this.server_version);
this.Item = prismarine_items(this.server_version);
}
export function initBot(username) { initBot(username) {
let bot = createBot({ let bot = createBot({
username: username, username: username,
host: this.server_host,
host: settings.host, port: this.server_port,
port: settings.port,
auth: settings.auth, auth: settings.auth,
version: this.server_version,
version: mc_version,
}); });
bot.loadPlugin(pathfinder); bot.loadPlugin(pathfinder);
bot.loadPlugin(pvp); bot.loadPlugin(pvp);
bot.loadPlugin(collectblock); bot.loadPlugin(collectblock);
bot.loadPlugin(autoEat); bot.loadPlugin(autoEat);
bot.loadPlugin(armorManager); // auto equip armor bot.loadPlugin(armorManager); // auto equip armor
return bot; return bot;
} }
export function isHuntable(mob) { isHuntable(mob) {
if (!mob || !mob.name) return false; if (!mob || !mob.name) return false;
const animals = ['chicken', 'cow', 'llama', 'mooshroom', 'pig', 'rabbit', 'sheep']; const animals = ['chicken', 'cow', 'llama', 'mooshroom', 'pig', 'rabbit', 'sheep'];
return animals.includes(mob.name.toLowerCase()) && !mob.metadata[16]; // metadata 16 is not baby return animals.includes(mob.name.toLowerCase()) && !mob.metadata[16]; // metadata 16 is not baby
} }
export function isHostile(mob) { isHostile(mob) {
if (!mob || !mob.name) return false; if (!mob || !mob.name) return false;
return (mob.type === 'mob' || mob.type === 'hostile') && mob.name !== 'iron_golem' && mob.name !== 'snow_golem'; return (mob.type === 'mob' || mob.type === 'hostile') && mob.name !== 'iron_golem' && mob.name !== 'snow_golem';
} }
export function getItemId(itemName) { getItemId(itemName) {
let item = mcdata.itemsByName[itemName]; let item = this.mcdata.itemsByName[itemName];
if (item) { if (item) {
return item.id; return item.id;
} }
return null; return null;
} }
export function getItemName(itemId) { getItemName(itemId) {
let item = mcdata.items[itemId] let item = this.mcdata.items[itemId]
if (item) { if (item) {
return item.name; return item.name;
} }
return null; return null;
} }
export function getBlockId(blockName) { getBlockId(blockName) {
let block = mcdata.blocksByName[blockName]; let block = this.mcdata.blocksByName[blockName];
if (block) { if (block) {
return block.id; return block.id;
} }
return null; return null;
} }
export function getBlockName(blockId) { getBlockName(blockId) {
let block = mcdata.blocks[blockId] let block = this.mcdata.blocks[blockId]
if (block) { if (block) {
return block.name; return block.name;
} }
return null; return null;
} }
export function getAllItems(ignore) { getAllItems(ignore) {
if (!ignore) { if (!ignore) {
ignore = []; ignore = [];
} }
let items = [] let items = []
for (const itemId in mcdata.items) { for (const itemId in this.mcdata.items) {
const item = mcdata.items[itemId]; const item = this.mcdata.items[itemId];
if (!ignore.includes(item.name)) { if (!ignore.includes(item.name)) {
items.push(item); items.push(item);
} }
@ -129,8 +101,8 @@ export function getAllItems(ignore) {
return items; return items;
} }
export function getAllItemIds(ignore) { getAllItemIds(ignore) {
const items = getAllItems(ignore); const items = this.getAllItems(ignore);
let itemIds = []; let itemIds = [];
for (const item of items) { for (const item of items) {
itemIds.push(item.id); itemIds.push(item.id);
@ -138,13 +110,13 @@ export function getAllItemIds(ignore) {
return itemIds; return itemIds;
} }
export function getAllBlocks(ignore) { getAllBlocks(ignore) {
if (!ignore) { if (!ignore) {
ignore = []; ignore = [];
} }
let blocks = [] let blocks = []
for (const blockId in mcdata.blocks) { for (const blockId in this.mcdata.blocks) {
const block = mcdata.blocks[blockId]; const block = this.mcdata.blocks[blockId];
if (!ignore.includes(block.name)) { if (!ignore.includes(block.name)) {
blocks.push(block); blocks.push(block);
} }
@ -152,8 +124,8 @@ export function getAllBlocks(ignore) {
return blocks; return blocks;
} }
export function getAllBlockIds(ignore) { getAllBlockIds(ignore) {
const blocks = getAllBlocks(ignore); const blocks = this.getAllBlocks(ignore);
let blockIds = []; let blockIds = [];
for (const block of blocks) { for (const block of blocks) {
blockIds.push(block.id); blockIds.push(block.id);
@ -161,18 +133,18 @@ export function getAllBlockIds(ignore) {
return blockIds; return blockIds;
} }
export function getAllBiomes() { getAllBiomes() {
return mcdata.biomes; return this.mcdata.biomes;
} }
export function getItemCraftingRecipes(itemName) { getItemCraftingRecipes(itemName) {
let itemId = getItemId(itemName); let itemId = this.getItemId(itemName);
if (!mcdata.recipes[itemId]) { if (!this.mcdata.recipes[itemId]) {
return null; return null;
} }
let recipes = []; let recipes = [];
for (let r of mcdata.recipes[itemId]) { for (let r of this.mcdata.recipes[itemId]) {
let recipe = {}; let recipe = {};
let ingredients = []; let ingredients = [];
if (r.ingredients) { if (r.ingredients) {
@ -181,7 +153,7 @@ export function getItemCraftingRecipes(itemName) {
ingredients = r.inShape.flat(); ingredients = r.inShape.flat();
} }
for (let ingredient of ingredients) { for (let ingredient of ingredients) {
let ingredientName = getItemName(ingredient); let ingredientName = this.getItemName(ingredient);
if (ingredientName === null) continue; if (ingredientName === null) continue;
if (!recipe[ingredientName]) if (!recipe[ingredientName])
recipe[ingredientName] = 0; recipe[ingredientName] = 0;
@ -193,12 +165,12 @@ export function getItemCraftingRecipes(itemName) {
return recipes; return recipes;
} }
export function isSmeltable(itemName) { isSmeltable(itemName) {
const misc_smeltables = ['beef', 'chicken', 'cod', 'mutton', 'porkchop', 'rabbit', 'salmon', 'tropical_fish', 'potato', 'kelp', 'sand', 'cobblestone', 'clay_ball']; 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); return itemName.includes('raw') || itemName.includes('log') || misc_smeltables.includes(itemName);
} }
export function getSmeltingFuel(bot) { getSmeltingFuel(bot) {
let fuel = bot.inventory.items().find(i => i.name === 'coal' || i.name === 'charcoal') let fuel = bot.inventory.items().find(i => i.name === 'coal' || i.name === 'charcoal')
if (fuel) if (fuel)
return fuel; return fuel;
@ -208,7 +180,7 @@ export function getSmeltingFuel(bot) {
return bot.inventory.items().find(i => i.name === 'coal_block' || i.name === 'lava_bucket'); return bot.inventory.items().find(i => i.name === 'coal_block' || i.name === 'lava_bucket');
} }
export function getFuelSmeltOutput(fuelName) { getFuelSmeltOutput(fuelName) {
if (fuelName === 'coal' || fuelName === 'charcoal') if (fuelName === 'coal' || fuelName === 'charcoal')
return 8; return 8;
if (fuelName.includes('log') || fuelName.includes('planks')) if (fuelName.includes('log') || fuelName.includes('planks'))
@ -220,7 +192,7 @@ export function getFuelSmeltOutput(fuelName) {
return 0; return 0;
} }
export function getItemSmeltingIngredient(itemName) { getItemSmeltingIngredient(itemName) {
return { return {
baked_potato: 'potato', baked_potato: 'potato',
steak: 'raw_beef', steak: 'raw_beef',
@ -238,10 +210,10 @@ export function getItemSmeltingIngredient(itemName) {
}[itemName]; }[itemName];
} }
export function getItemBlockSources(itemName) { getItemBlockSources(itemName) {
let itemId = getItemId(itemName); let itemId = this.getItemId(itemName);
let sources = []; let sources = [];
for (let block of getAllBlocks()) { for (let block of this.getAllBlocks()) {
if (block.drops.includes(itemId)) { if (block.drops.includes(itemId)) {
sources.push(block.name); sources.push(block.name);
} }
@ -249,7 +221,7 @@ export function getItemBlockSources(itemName) {
return sources; return sources;
} }
export function getItemAnimalSource(itemName) { getItemAnimalSource(itemName) {
return { return {
raw_beef: 'cow', raw_beef: 'cow',
raw_chicken: 'chicken', raw_chicken: 'chicken',
@ -263,16 +235,16 @@ export function getItemAnimalSource(itemName) {
}[itemName]; }[itemName];
} }
export function getBlockTool(blockName) { getBlockTool(blockName) {
let block = mcdata.blocksByName[blockName]; let block = this.mcdata.blocksByName[blockName];
if (!block || !block.harvestTools) { if (!block || !block.harvestTools) {
return null; return null;
} }
return getItemName(Object.keys(block.harvestTools)[0]); // Double check first tool is always simplest return this.getItemName(Object.keys(block.harvestTools)[0]); // Double check first tool is always simplest
} }
export function makeItem(name, amount=1) { makeItem(name, amount=1) {
return new Item(getItemId(name), amount); return new this.Item(this.getItemId(name), amount);
} }
/** /**
@ -281,19 +253,19 @@ export function makeItem(name, amount=1) {
* @param {Recipe} recipe * @param {Recipe} recipe
* @returns {Object<mc.ItemName, number>} an object describing the number of each ingredient. * @returns {Object<mc.ItemName, number>} an object describing the number of each ingredient.
*/ */
export function ingredientsFromPrismarineRecipe(recipe) { ingredientsFromPrismarineRecipe(recipe) {
let requiredIngedients = {}; let requiredIngedients = {};
if (recipe.inShape) if (recipe.inShape)
for (const ingredient of recipe.inShape.flat()) { for (const ingredient of recipe.inShape.flat()) {
if(ingredient.id<0) continue; //prismarine-recipe uses id -1 as an empty crafting slot if(ingredient.id<0) continue; //prismarine-recipe uses id -1 as an empty crafting slot
const ingredientName = getItemName(ingredient.id); const ingredientName = this.getItemName(ingredient.id);
requiredIngedients[ingredientName] ??=0; requiredIngedients[ingredientName] ??=0;
requiredIngedients[ingredientName] += ingredient.count; requiredIngedients[ingredientName] += ingredient.count;
} }
if (recipe.ingredients) if (recipe.ingredients)
for (const ingredient of recipe.ingredients) { for (const ingredient of recipe.ingredients) {
if(ingredient.id<0) continue; if(ingredient.id<0) continue;
const ingredientName = getItemName(ingredient.id); const ingredientName = this.getItemName(ingredient.id);
requiredIngedients[ingredientName] ??=0; requiredIngedients[ingredientName] ??=0;
requiredIngedients[ingredientName] -= ingredient.count; requiredIngedients[ingredientName] -= ingredient.count;
//Yes, the `-=` is intended. //Yes, the `-=` is intended.
@ -311,7 +283,7 @@ export function ingredientsFromPrismarineRecipe(recipe) {
* @param {boolean} discrete - Is the action discrete? * @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'}` * @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) { calculateLimitingResource(availableItems, requiredItems, discrete=true) {
let limitingResource = null; let limitingResource = null;
let num = Infinity; let num = Infinity;
for (const itemType in requiredItems) { for (const itemType in requiredItems) {
@ -323,3 +295,45 @@ export function calculateLimitingResource(availableItems, requiredItems, discret
if(discrete) num = Math.floor(num); if(discrete) num = Math.floor(num);
return {num, limitingResource} 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();

View file

@ -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';
@ -82,3 +83,46 @@ export async function findServers(ip, earlyExit = false, timeout = 100) {
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;
}