PrismarineJS / mineflayer

Create Minecraft bots with a powerful, stable, and high level JavaScript API.
https://prismarinejs.github.io/mineflayer/
MIT License
4.95k stars 904 forks source link

(1.18.2) Bot can't look anywhere (bot.lookAt() method) #3024

Closed SonicandTailsCD closed 1 year ago

SonicandTailsCD commented 1 year ago

Versions

Detailed description of a problem

A clear and concise description of what the problem is, with as much context as possible. The bot cannot look anywhere. I see that point.minus, which is in Mineflayer and not in my code, isn't a function/doesn't work. What are you building? A bot that does a whole load of things... What problem are you trying to solve? I want the bot to reset its location when done

What did you try yet?

Did you try any method from the API? What is the API? :P Did you try any example? No, not yet.

Your current code

// this is bot.ts
// Here I tell node.js mineflayer and a few other plugins are required to load
import { createBot } from 'mineflayer';
import pathfinderPkg from 'mineflayer-pathfinder';
import { botStates, commands, setup as setupCmds } from './lib/commands.js';
import { setup as setupMfUtils } from './lib/mineflayer-utils.js';
const { pathfinder, Movements } = pathfinderPkg;

// Here, we set up the server connection (in this case, my skin plugin server)
console.log('Registering the bot and allowing node to control it...')
export const bot = createBot({
    host: 'SonicJavaBots.aternos.me',
    port: 37867,
    username: 'SonicandTailsCDb',
    version: '1.18.2'
});
console.log('Bot client initialized!');
console.log('Node.JS can control the bot now.')
console.log('Setting up MfUtils and Cmds')
setupMfUtils(bot);
setupCmds(bot);
console.log('Done :)')

// Setup pathfinder
console.log('Setting up pathfinder...')
bot.loadPlugin(pathfinder);
const movements = new Movements(bot, bot.registry);
bot.pathfinder.setMovements(movements);
bot.pathfinder.thinkTimeout = 10000;
bot.pathfinder.tickTimeout = 22
console.log('Done :)')

// And now, we work on the AI here!
// Here's all of the functions:
// One note tho: All functions are placeholders.
// function onPhysicsTick (): void {}

console.log('Registering function...')
function onSpawn (): void {
    bot.chat('/skin set SonicandTailsCDb robot1_alextest');
    console.log('Bot initialized completely :)');
    bot.chat("Hey! I'm working properly :D");

    bot.on('chat', async (daname, msg) => {
        if (daname === 'SkinsRestorer') return;
        console.log(daname + ' said: ' + msg);
        if (msg === 'follow me') {
            while (commands.followMe(daname)) {
                console.log('I started following ' + daname);
                return;
            }
        }
        if (msg === 'hello') {
            bot.chat('Hi! :)')
        }
        if (msg === 'Reset your viewing location') {
            await bot.chat('Sure, I\'ll do that :)')
            await bot.waitForTicks(10)
            commands.resetViewingLocation()
        }
        if (msg === 'Where are you, bot?') {
            console.log('Bot is giving its location to ' + daname + '!')
            commands.location(daname)
        }
        if (msg === 'Sleep with me :)') {
            await commands.sleep();
        }
        if (msg === 'eat with me') {
            commands.eatWithPlayer(daname)
        }
        if (msg === 'CLEEANN!') {
            await commands.mineAround();
        }
        if (msg === 'Stop cleaning') {
            await commands.stopMining();
        }
        if (msg === 'Protect me :)') {
            try {
                commands.protectMe(daname)
            }
            catch (err) {
                console.log(String(err?.message))
            }
            console.log('Bot instructed to protect ' + daname + ', obeying player...')
        }
        if (msg === 'attack me') {
            const message = "Alright, run while you still can!"
            bot.chat(message)
            commands.attackPlayer(daname)
        }
        if (msg === 'Stop attacking me') {
            commands.stopAttacking()
        }
        if (msg === 'attack any entity') {
            commands.attackEntity()
        }
        if (msg === 'Stop server') {
            bot.chat('Okay!')
            await bot.waitForTicks(60)
            bot.chat('/stop')
        }
        if (msg === 'hey') {
            bot.chat('what you want?');
            console.log('I said: what you want?');
        }
        if (msg === 'Say hi to 678435021') {
            bot.chat("Oh, I'm sorry! Hi 678435021! :)")
        }
        if (msg === 'stop following me') {
            commands.unFollowMe();
            console.log('I stopped following ' + daname);
        }
    });
}
console.log('Done :)')
// I'm unsure on how am I gonna use onPhysicsTick function but I'll leave it there in case me or @678435021 wanna use it.
// bot.on('physicsTick', onPhysicsTick);

// Next, I'm gonna set spawn actions.
console.log('Running now!')
bot.once('spawn', onSpawn);

// now commands.ts
import { lookAtEntity } from './mineflayer-utils.js';
import { sleep } from './sleep.js';
import pathfinderPkg from 'mineflayer-pathfinder';
import { Bot } from 'mineflayer';
const { goals } = pathfinderPkg;

let bot: Bot;
export function setup (_bot: Bot): void {
    bot = _bot;
}
let target: any = null;
let player = null;
export const protectUser = null;
// 678435021: Add more here when needed
// SonicandTailsCD: Alright, I will :P
export const botStates = {
    moving: false,
    looking: false,
    mining: false,
    following: false,
    mentionedEatingWithPlayerAlready: false,
    attacking: false,
    guarding: false
};

// Add here any values when needed.
export const values = {
    range: 3,
    BlocksAwayFromTarget: 3,
    entities: []
};

export const commands = {
    async sleep () {
        try {
            console.log('Sleeping!');
            bot.chat("I'm coming :D");
            const bed = bot.findBlock({ matching: block => bot.isABed(block) });
            if (!bed) {
                console.log("Sorry, I can't find a bed!");
                return;
            }
            await bot.sleep(bed);
        }
        catch(err) {
            bot.chat('Sorry, I couldn\'t sleep! Please check the console log.');
            console.log(String(err?.message));
        }
    },
    async location (daname: string) {
        bot.chat(daname + ", I\'m at " + bot.entity.position)
    },
    async stopMining () {
        bot.chat('Okay! I\'ll stop.');
        botStates.mining = false;
    },
    async attackPlayer (daname: string) {
        const player = bot.players[daname]
        if (!player || !player.entity) {
            bot.chat('I can\'t see you')
        } 
        else {
            bot.chat(`Attacking ${player.username}`)
            botStates.attacking = true
            while (botStates.attacking = true) {
                await bot.waitForTicks(5)
                bot.attack(player.entity)
            }
        }
    },
    async stopAttacking () {
        botStates.attacking = false
        bot.chat('Okay, I won\'t attack you anymore.')
    },
    async attackEntity () {
        const entity = bot.nearestEntity()
        if (!entity) {
          bot.chat('No nearby entities')
        } else {
          bot.chat('Attacking ${entity.name ?? entity.username}')
          bot.attack(entity)
        }
    },
    async eatWithPlayer(daname: string) {
        this.followMe(daname)
        if (botStates.mentionedEatingWithPlayerAlready = true) {
            console.log('Already mentioned coming to player!')
        } 
        else {
            bot.chat('Let me come to you first! :D')
            botStates.mentionedEatingWithPlayerAlready = true
        }
        if (botStates.looking = true) {
            const eatitem: any = bot.inventory.items().find(item => item.name === 'Suspicious Stew')
            const eatTime = 1500
            try {
                await bot.equip(eatitem, 'hand')
                bot.chat("Sorry, I'm still being worked on. I cannot eat yet.")
            }
            catch (err) {
                bot.chat(String(err?.message))
            }
            bot.activateItem()
            await sleep(eatTime)
            bot.deactivateItem()
            botStates.mentionedEatingWithPlayerAlready = false;

        }
        else {
            bot.waitForTicks(200)
            botStates.mentionedEatingWithPlayerAlready = true
            this.eatWithPlayer(daname)
        }
    },
    async mineAround () {
        if (botStates.mining) {
            return;
        }

        bot.chat('Anything for you! :)');

        botStates.mining = true;
        while (botStates.mining) {
            await bot.waitForTicks(1);
            const grassBlock = bot.findBlock({
                matching: block => {
                    return block.name === 'grass' || block.name === 'tall_grass';
                }
            });

            if (!grassBlock) {
                console.log("Couldn't find grass.");
                await sleep(100);
                continue;
            }

            try {
                await bot.pathfinder.goto(
                    new goals.GoalLookAtBlock(
                        grassBlock.position, bot.world, {
                            reach: 2.5,
                            entityHeight: bot.player.entity.height
                        }
                    )
                );
            } 
            catch (e) {
                continue;
            }

            await bot.dig(grassBlock, true);
        }
    },
    async speakProtect () {
        bot.chat('Coming now :D')
    },
    async resetViewingLocation () {
        await bot.lookAt(45, 0);
        await bot.lookAt(0, 0);
    },
    async protectMe (daname: string) {
        botStates.guarding = true
        if (botStates.following = true) {
            console.log('Already following a player, skipping follow activation!')
            if (player =! daname) {
                bot.chat('You\'re not the one I\'m following! I\'m not obeying you. >:(')
                return
            }
            const protectUser = daname;
            this.speakProtect()
        }
        else {
            console.log('Since the bot isn\'t following anything, the bot will begin following ' + daname)
            this.followMe(daname)
            this.speakProtect()
            const player = bot.players[daname];
            const protectUser = player
        }
        bot.on('entityHurt', async (entity)=>{
            target = entity
            try {
                if (botStates.attacking = true) return
                if (target.username != protectUser) return
                botStates.attacking = true
                await bot.setControlState('forward', true)
                await bot.setControlState('sprint', true)
                const location = target.position
                botStates.guarding = false
                while (botStates.attacking = true) {
                    botStates.following = false
                    await bot.waitForTicks(5)
                    let distance = bot.entity.position.xzDistanceTo(location)
                    while (distance = values.BlocksAwayFromTarget) {
                        await bot.attack(target)
                        bot.lookAt(location)
                    }
                }
            }
            catch (err) {
                console.log('The bot wasn\'t able to help ' + protectUser + ' fight! :(')
            }
        })
        bot.on('entityGone', async (entity)=>{
            if (entity = target) {
                botStates.attacking = false
                botStates.guarding = true
                botStates.following = true
            }
            else return
        })
    },
    async followMe (daname: string) {
        botStates.following = true;

        const player = bot.players[daname];
        if (botStates.moving) {
            bot.chat("Sorry, can't run this command more than once!");
        }
        botStates.moving = true;

        if (!player?.entity) {
            bot.chat("I can't see you, " + daname);
            botStates.moving = false;
            return;
        }

        bot.chat('Okay ' + daname);

        while (botStates.following) {
            try {
                if (bot.entity.position.distanceTo(player.entity.position) + 0.15 <= values.range) {
                    await lookAtEntity(player.entity, true);
                    botStates.looking = true;
                }
                else {
                    botStates.looking = false;
                }
                await sleep(200);
                const goal = new goals.GoalFollow(player.entity, values.range);
                await bot.pathfinder.goto(goal);
            } 
            catch (err) {
                console.log(String(err?.message));
            }
        }
    },
    unFollowMe () {
        if (!botStates.moving) {
            return;
        }
        botStates.moving = false;
        botStates.following = false;
        bot.pathfinder.stop();
    }
};

Expected behavior

A clear and concise description of what you expected to happen. I expected the bot to look at a specific location, to reset where its looking.

Additional context

Add any other context about the problem here. I'd like help ASAP. Thanks :) It seems like the issue is coming from Mineflayer plugin itself, the error is: /Users/adm/Downloads/tteeesstt-main/tteeesstt/node_modules/mineflayer/lib/plugins/physics.js:255 const delta = point.minus(bot.entity.position.offset(0, bot.entity.height, 0)) ^

TypeError: point.minus is not a function at EventEmitter.bot.lookAt (/Users/adm/Downloads/tteeesstt-main/tteeesstt/node_modules/mineflayer/lib/plugins/physics.js:255:25) at Object.resetViewingLocation (file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/lib/commands.js:142:19) at EventEmitter. (file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/bot.js:47:22)

kashalls commented 1 year ago

bot.lookAt requires a Vec3 instance (x, y, z), you are only passing an x and y value and not even in the right instance.

Please read the documentation on how to use this: https://prismarinejs.github.io/mineflayer/#/api?id=botlookatpoint-force

SonicandTailsCD commented 1 year ago

I tried that but it still gives me the error. (I'm using TypeScript)

SonicandTailsCD commented 1 year ago

point.minus isn't an operation Node can do.

kashalls commented 1 year ago

point.minus isn't an operation Node can do.

The minus function becomes available when you pass a Vec3 instance, as shown here: https://github.com/PrismarineJS/node-vec3

SonicandTailsCD commented 1 year ago

This doesn't help, there's an error: file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/bot.js:6 var Vec3 = require('vec3'); ^

ReferenceError: require is not defined in ES module scope, you can use import instead This file is being treated as an ES module because it has a '.js' file extension and '/Users/adm/Downloads/tteeesstt-main/tteeesstt/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension. at file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/bot.js:6:12 at ModuleJob.run (node:internal/modules/esm/module_job:198:25) at async Promise.all (index 0) at async ESMLoader.import (node:internal/modules/esm/loader:409:24) at async loadESM (node:internal/process/esm_loader:85:5) at async handleMainPromise (node:internal/modules/run_main:61:12)

Node.js v17.9.1

SonicandTailsCD commented 1 year ago

And from the TypeScript compiler: src/lib/commands.ts:6:18 - error TS7016: Could not find a declaration file for module 'vec3'. '/Users/adm/Downloads/tteeesstt-main/tteeesstt/node_modules/vec3/wrapper.mjs' implicitly has an 'any' type. There are types at '/Users/adm/Downloads/tteeesstt-main/tteeesstt/node_modules/vec3/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'vec3' library may need to update its package.json or typings.

kashalls commented 1 year ago

This doesn't help, there's an error: file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/bot.js:6 var Vec3 = require('vec3'); ^

ReferenceError: require is not defined in ES module scope, you can use import instead This file is being treated as an ES module because it has a '.js' file extension and '/Users/adm/Downloads/tteeesstt-main/tteeesstt/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension. at file:///Users/adm/Downloads/tteeesstt-main/tteeesstt/dist/bot.js:6:12 at ModuleJob.run (node:internal/modules/esm/module_job:198:25) at async Promise.all (index 0) at async ESMLoader.import (node:internal/modules/esm/loader:409:24) at async loadESM (node:internal/process/esm_loader:85:5) at async handleMainPromise (node:internal/modules/run_main:61:12)

Node.js v17.9.1

You can't use require in a module package. You would need to import it instead. See: https://nodejs.org/api/esm.html#import-specifiers

There are types at '/Users/adm/Downloads/tteeesstt-main/tteeesstt/node_modules/vec3/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'vec3' library may need to update its package.json or typings.

You're correct. I'm not sure how to fix this as I don't work with TS myself. This would be a good idea to look into and make it typescript compatible.

SonicandTailsCD commented 1 year ago

You can't use require in a module package. You would need to import it instead. See: https://nodejs.org/api/esm.html#import-specifiers

I understand.

You're correct. I'm not sure how to fix this as I don't work with TS myself. This would be a good idea to look into and make it typescript compatible.

I believe @678435021 knows TypeScript, but I've tried to send a message to him and no answer :(

SonicandTailsCD commented 1 year ago

What should we do?

kashalls commented 1 year ago

What should we do?

You'll have to either:

A) Wait until someone else fixes it. B) Fix it, test it, and push it down the pipeline.

SonicandTailsCD commented 1 year ago

Hm. Okay...

SonicandTailsCD commented 1 year ago

I guess I'll go with B for now.

SonicandTailsCD commented 1 year ago

I fixed it, but how weird that it didn't work at the beggining. Here's the code:

// bot.ts
// Here I tell node.js mineflayer and a few other plugins are required to load
import { createBot } from 'mineflayer';
import pathfinderPkg from 'mineflayer-pathfinder';
import { botStates, commands, setup as setupCmds } from './lib/commands.js';
import { setup as setupMfUtils } from './lib/mineflayer-utils.js';
const { pathfinder, Movements } = pathfinderPkg;

// Here, we set up the server connection (in this case, my skin plugin server)
console.log('Registering the bot and allowing node to control it...')
export const bot = createBot({
    host: 'SonicJavaBots.aternos.me',
    port: 37867,
    username: 'SonicandTailsCDb',
    version: '1.18.2'
});
console.log('Bot client initialized!');
console.log('Node.JS can control the bot now.')
console.log('Setting up MfUtils and Cmds')
setupMfUtils(bot);
setupCmds(bot);
console.log('Done :)')

// Setup pathfinder
console.log('Setting up pathfinder...')
bot.loadPlugin(pathfinder);
const movements = new Movements(bot, bot.registry);
bot.pathfinder.setMovements(movements);
bot.pathfinder.thinkTimeout = 10000;
bot.pathfinder.tickTimeout = 22
console.log('Done :)')

// And now, we work on the AI here!
// Here's all of the functions:
// One note tho: All functions are placeholders.
// function onPhysicsTick (): void {}

console.log('Registering function...')
function onSpawn (): void {
    bot.chat('/skin set SonicandTailsCDb robot1_alextest');
    console.log('Bot initialized completely :)');
    bot.chat("Hey! I'm working properly :D");

    bot.on('chat', async (daname, msg) => {
        if (daname === 'SkinsRestorer') return;
        console.log(daname + ' said: ' + msg);
        if (msg === 'follow me') {
            while (commands.followMe(daname)) {
                console.log('I started following ' + daname);
                return;
            }
        }
        if (msg === 'hello') {
            bot.chat('Hi! :)')
        }
        if (msg === 'Reset your viewing location') {
            await bot.chat('Sure, I\'ll do that :)')
            await bot.waitForTicks(10)
            commands.resetViewingLocation()
        }
        if (msg === 'Where are you, bot?') {
            console.log('Bot is giving its location to ' + daname + '!')
            commands.location(daname)
        }
        if (msg === 'Sleep with me :)') {
            await commands.sleep();
        }
        if (msg === 'eat with me') {
            commands.eatWithPlayer(daname)
        }
        if (msg === 'CLEEANN!') {
            await commands.mineAround();
        }
        if (msg === 'Stop cleaning') {
            await commands.stopMining();
        }
        if (msg === 'Protect me :)') {
            try {
                commands.protectMe(daname)
            }
            catch (err) {
                console.log(String(err?.message))
            }
            console.log('Bot instructed to protect ' + daname + ', obeying player...')
        }
        if (msg === 'attack me') {
            const message = "Alright, run while you still can!"
            bot.chat(message)
            commands.attackPlayer(daname)
        }
        if (msg === 'Stop attacking me') {
            commands.stopAttacking()
        }
        if (msg === 'attack any entity') {
            commands.attackEntity()
        }
        if (msg === 'Stop server') {
            bot.chat('Okay!')
            await bot.waitForTicks(60)
            bot.chat('/stop')
        }
        if (msg === 'hey') {
            bot.chat('what you want?');
            console.log('I said: what you want?');
        }
        if (msg === 'Say hi to 678435021') {
            bot.chat("Oh, I'm sorry! Hi 678435021! :)")
        }
        if (msg === 'stop following me') {
            commands.unFollowMe();
            console.log('I stopped following ' + daname);
        }
    });
}
console.log('Done :)')
// I'm unsure on how am I gonna use onPhysicsTick function but I'll leave it there in case me or @678435021 wanna use it.
// bot.on('physicsTick', onPhysicsTick);

// Next, I'm gonna set spawn actions.
console.log('Running now!')
bot.once('spawn', onSpawn);

// commands.ts
import { lookAtEntity } from './mineflayer-utils.js';
import { sleep } from './sleep.js';
import pathfinderPkg from 'mineflayer-pathfinder';
import { Bot } from 'mineflayer';
const { goals } = pathfinderPkg;
import Vec3 from 'vec3';
let bot: Bot;
export function setup (_bot: Bot): void {
    bot = _bot;
}
let target: any = null;
let player = null;
export const protectUser = null;
// 678435021: Add more here when needed
// SonicandTailsCD: Alright, I will :P
export const botStates = {
    moving: false,
    looking: false,
    mining: false,
    following: false,
    mentionedEatingWithPlayerAlready: false,
    attacking: false,
    guarding: false
};

// Add here any values when needed.
export const values = {
    range: 3,
    BlocksAwayFromTarget: 3,
    entities: []
};

export const commands = {
    async sleep () {
        try {
            console.log('Sleeping!');
            bot.chat("I'm coming :D");
            const bed = bot.findBlock({ matching: block => bot.isABed(block) });
            if (!bed) {
                console.log("Sorry, I can't find a bed!");
                return;
            }
            await bot.sleep(bed);
        }
        catch(err) {
            bot.chat('Sorry, I couldn\'t sleep! Please check the console log.');
            console.log(String(err?.message));
        }
    },
    async location (daname: string) {
        bot.chat(daname + ", I\'m at " + bot.entity.position)
    },
    async stopMining () {
        bot.chat('Okay! I\'ll stop.');
        botStates.mining = false;
    },
    async attackPlayer (daname: string) {
        const player = bot.players[daname]
        if (!player || !player.entity) {
            bot.chat('I can\'t see you')
        } 
        else {
            bot.chat(`Attacking ${player.username}`)
            botStates.attacking = true
            while (botStates.attacking = true) {
                await bot.waitForTicks(5)
                bot.attack(player.entity)
            }
        }
    },
    async stopAttacking () {
        botStates.attacking = false
        bot.chat('Okay, I won\'t attack you anymore.')
    },
    async attackEntity () {
        const entity = bot.nearestEntity()
        if (!entity) {
          bot.chat('No nearby entities')
        } else {
          bot.chat('Attacking ${entity.name ?? entity.username}')
          bot.attack(entity)
        }
    },
    async eatWithPlayer(daname: string) {
        this.followMe(daname)
        if (botStates.mentionedEatingWithPlayerAlready = true) {
            console.log('Already mentioned coming to player!')
        } 
        else {
            bot.chat('Let me come to you first! :D')
            botStates.mentionedEatingWithPlayerAlready = true
        }
        if (botStates.looking = true) {
            const eatitem: any = bot.inventory.items().find(item => item.name === 'Suspicious Stew')
            const eatTime = 1500
            try {
                await bot.equip(eatitem, 'hand')
                bot.chat("Sorry, I'm still being worked on. I cannot eat yet.")
            }
            catch (err) {
                bot.chat(String(err?.message))
            }
            bot.activateItem()
            await sleep(eatTime)
            bot.deactivateItem()
            botStates.mentionedEatingWithPlayerAlready = false;

        }
        else {
            bot.waitForTicks(200)
            botStates.mentionedEatingWithPlayerAlready = true
            this.eatWithPlayer(daname)
        }
    },
    async mineAround () {
        if (botStates.mining) {
            return;
        }

        bot.chat('Anything for you! :)');

        botStates.mining = true;
        while (botStates.mining) {
            await bot.waitForTicks(1);
            const grassBlock = bot.findBlock({
                matching: block => {
                    return block.name === 'grass' || block.name === 'tall_grass';
                }
            });

            if (!grassBlock) {
                console.log("Couldn't find grass.");
                await sleep(100);
                continue;
            }

            try {
                await bot.pathfinder.goto(
                    new goals.GoalLookAtBlock(
                        grassBlock.position, bot.world, {
                            reach: 2.5,
                            entityHeight: bot.player.entity.height
                        }
                    )
                );
            } 
            catch (e) {
                continue;
            }

            await bot.dig(grassBlock, true);
        }
    },
    async speakProtect () {
        bot.chat('Coming now :D')
    },
    async resetViewingLocation () {
        const location1 = new Vec3(45, 0, 0)
        const location2 = new Vec3(0, 0, 0)
        await bot.lookAt(location1);
        await bot.lookAt(location2);
    },
    async protectMe (daname: string) {
        botStates.guarding = true
        if (botStates.following = true) {
            console.log('Already following a player, skipping follow activation!')
            if (player =! daname) {
                bot.chat('You\'re not the one I\'m following! I\'m not obeying you. >:(')
                return
            }
            const protectUser = daname;
            this.speakProtect()
        }
        else {
            console.log('Since the bot isn\'t following anything, the bot will begin following ' + daname)
            this.followMe(daname)
            this.speakProtect()
            const player = bot.players[daname];
            const protectUser = player
        }
        bot.on('entityHurt', async (entity)=>{
            target = entity
            try {
                if (botStates.attacking = true) return
                if (target.username != protectUser) return
                botStates.attacking = true
                await bot.setControlState('forward', true)
                await bot.setControlState('sprint', true)
                const location = target.position
                botStates.guarding = false
                while (botStates.attacking = true) {
                    botStates.following = false
                    await bot.waitForTicks(5)
                    let distance = bot.entity.position.xzDistanceTo(location)
                    while (distance = values.BlocksAwayFromTarget) {
                        await bot.attack(target)
                        bot.lookAt(location)
                    }
                }
            }
            catch (err) {
                console.log('The bot wasn\'t able to help ' + protectUser + ' fight! :(')
            }
        })
        bot.on('entityGone', async (entity)=>{
            if (entity = target) {
                botStates.attacking = false
                botStates.guarding = true
                botStates.following = true
            }
            else return
        })
    },
    async followMe (daname: string) {
        botStates.following = true;

        const player = bot.players[daname];
        if (botStates.moving) {
            bot.chat("Sorry, can't run this command more than once!");
        }
        botStates.moving = true;

        if (!player?.entity) {
            bot.chat("I can't see you, " + daname);
            botStates.moving = false;
            return;
        }

        bot.chat('Okay ' + daname);

        while (botStates.following) {
            try {
                if (bot.entity.position.distanceTo(player.entity.position) + 0.15 <= values.range) {
                    await lookAtEntity(player.entity, true);
                    botStates.looking = true;
                }
                else {
                    botStates.looking = false;
                }
                await sleep(200);
                const goal = new goals.GoalFollow(player.entity, values.range);
                await bot.pathfinder.goto(goal);
            } 
            catch (err) {
                console.log(String(err?.message));
            }
        }
    },
    unFollowMe () {
        if (!botStates.moving) {
            return;
        }
        botStates.moving = false;
        botStates.following = false;
        bot.pathfinder.stop();
    }
};