mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-03 22:05:35 +02:00
commit
a740fe797e
9 changed files with 98 additions and 50 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Kolby Nottingham
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -3,5 +3,5 @@
|
|||
"host": "localhost",
|
||||
"port": 55916,
|
||||
"auth": "offline",
|
||||
"allow_insecure_coding": true
|
||||
"allow_insecure_coding": false
|
||||
}
|
|
@ -29,7 +29,7 @@ export class Coder {
|
|||
|
||||
// write custom code to file and import it
|
||||
async stageCode(code) {
|
||||
code = this.santitizeCode(code);
|
||||
code = this.sanitizeCode(code);
|
||||
let src = '';
|
||||
code = code.replaceAll('console.log(', 'log(bot,');
|
||||
code = code.replaceAll('log("', 'log(bot,"');
|
||||
|
@ -62,7 +62,8 @@ export class Coder {
|
|||
return await import('../..' + this.fp + filename);
|
||||
}
|
||||
|
||||
santitizeCode(code) {
|
||||
sanitizeCode(code) {
|
||||
code = code.trim();
|
||||
const remove_strs = ['Javascript', 'javascript', 'js']
|
||||
for (let r of remove_strs) {
|
||||
if (code.startsWith(r)) {
|
||||
|
@ -93,13 +94,14 @@ export class Coder {
|
|||
let res = await this.generateCodeLoop(agent_history);
|
||||
this.generating = false;
|
||||
if (!res.interrupted) this.agent.bot.emit('idle');
|
||||
return res.message;
|
||||
}
|
||||
|
||||
async generateCodeLoop(agent_history) {
|
||||
let system_message = "You are a minecraft mineflayer bot that plays minecraft by writing javascript codeblocks. Given the conversation between you and the user, use the provided skills and world functions to write your code in a codeblock. Example response: ``` // your code here ``` You will then be given a response to your code. If you are satisfied with the response, respond without a codeblock in a conversational way. If something went wrong, write another codeblock and try to fix the problem.";
|
||||
system_message += getSkillDocs();
|
||||
|
||||
system_message += "\n\nExamples:\nUser zZZn98: come here \nAssistant: I am going to navigate to zZZn98. ```\nawait skills.goToPlayer(bot, 'zZZn98');```\nSystem: Code execution finished successfully.\nAssistant: Done.";
|
||||
system_message += "\n\nExamples:\nUser zZZn98: come here \nAssistant: I am going to navigate to zZZn98. ```\nawait skills.goToPlayer(bot, 'zZZn98', 3);```\nSystem: Code execution finished successfully.\nAssistant: Done.";
|
||||
|
||||
let messages = await agent_history.getHistory(this.examples);
|
||||
|
||||
|
@ -107,7 +109,7 @@ export class Coder {
|
|||
let failures = 0;
|
||||
for (let i=0; i<5; i++) {
|
||||
if (this.agent.bot.interrupt_code)
|
||||
return;
|
||||
return {success: true, message: null, interrupted: true, timedout: false};
|
||||
console.log(messages)
|
||||
let res = await sendRequest(messages, system_message);
|
||||
console.log('Code generation response:', res)
|
||||
|
@ -120,8 +122,7 @@ export class Coder {
|
|||
return {success: true, message: null, interrupted: false, timedout: false};
|
||||
}
|
||||
if (failures >= 1) {
|
||||
agent_history.add('system', 'Action failed, agent would not write code.');
|
||||
return {success: false, message: null, interrupted: false, timedout: false};
|
||||
return {success: false, message: 'Action failed, agent would not write code.', interrupted: false, timedout: false};
|
||||
}
|
||||
messages.push({
|
||||
role: 'system',
|
||||
|
@ -143,7 +144,7 @@ export class Coder {
|
|||
|
||||
if (code_return.interrupted && !code_return.timedout)
|
||||
return {success: false, message: null, interrupted: true, timedout: false};
|
||||
console.log(code_return.message);
|
||||
console.log("Code generation result:", code_return.success, code_return.message);
|
||||
|
||||
messages.push({
|
||||
role: 'assistant',
|
||||
|
@ -219,7 +220,7 @@ export class Coder {
|
|||
formatOutput(bot) {
|
||||
if (bot.interrupt_code && !this.timedout) return '';
|
||||
let output = bot.output;
|
||||
const MAX_OUT = 1000;
|
||||
const MAX_OUT = 500;
|
||||
if (output.length > MAX_OUT) {
|
||||
output = `Code output is very long (${output.length} chars) and has been shortened.\n
|
||||
First outputs:\n${output.substring(0, MAX_OUT/2)}\n...skipping many lines.\nFinal outputs:\n ${output.substring(output.length - MAX_OUT/2)}`;
|
||||
|
|
|
@ -26,7 +26,7 @@ export const actionsList = [
|
|||
perform: async function (agent) {
|
||||
if (!settings.allow_insecure_coding)
|
||||
return 'Agent is not allowed to write code.';
|
||||
await agent.coder.generateCode(agent.history);
|
||||
return await agent.coder.generateCode(agent.history);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ export const actionsList = [
|
|||
}
|
||||
},
|
||||
{
|
||||
name: '!clear',
|
||||
name: '!clearChat',
|
||||
description: 'Clear the chat history.',
|
||||
perform: async function (agent) {
|
||||
agent.history.clear();
|
||||
|
@ -73,18 +73,24 @@ export const actionsList = [
|
|||
},
|
||||
{
|
||||
name: '!goToPlayer',
|
||||
description: 'Go to the given player. Ex: !goToPlayer("steve")',
|
||||
params: {'player_name': '(string) The name of the player to go to.'},
|
||||
perform: wrapExecution(async (agent, player_name) => {
|
||||
return await skills.goToPlayer(agent.bot, player_name);
|
||||
description: 'Go to the given player. Ex: !goToPlayer("steve", 3)',
|
||||
params: {
|
||||
'player_name': '(string) The name of the player to go to.',
|
||||
'closeness': '(number) How close to get to the player.'
|
||||
},
|
||||
perform: wrapExecution(async (agent, player_name, closeness) => {
|
||||
return await skills.goToPlayer(agent.bot, player_name, closeness);
|
||||
})
|
||||
},
|
||||
{
|
||||
name: '!followPlayer',
|
||||
description: 'Endlessly follow the given player. Will defend that player if self_defense mode is on. Ex: !followPlayer("stevie")',
|
||||
params: {'player_name': '(string) The name of the player to follow.'},
|
||||
perform: wrapExecution(async (agent, player_name) => {
|
||||
await skills.followPlayer(agent.bot, player_name);
|
||||
description: 'Endlessly follow the given player. Will defend that player if self_defense mode is on. Ex: !followPlayer("stevie", 4)',
|
||||
params: {
|
||||
'player_name': '(string) The name of the player to follow.',
|
||||
'follow_dist': '(number) The distance to follow from.'
|
||||
},
|
||||
perform: wrapExecution(async (agent, player_name, follow_dist) => {
|
||||
await skills.followPlayer(agent.bot, player_name, follow_dist);
|
||||
}, -1, 'followPlayer')
|
||||
},
|
||||
{
|
||||
|
|
|
@ -59,7 +59,7 @@ export const queryList = [
|
|||
}
|
||||
},
|
||||
{
|
||||
name: "!blocks",
|
||||
name: "!nearbyBlocks",
|
||||
description: "Get the blocks near the bot.",
|
||||
perform: function (agent) {
|
||||
let bot = agent.bot;
|
||||
|
|
|
@ -29,8 +29,8 @@ export class History {
|
|||
}
|
||||
|
||||
getSystemMessage() {
|
||||
let system_message = `You are a playful Minecraft bot named '${this.name}' that can communicate with players, see, move, mine, build, and interact with the world by using commands. Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your responses, use commands often, and do not give instructions unless asked.
|
||||
Don't pretend to act, use commands immediately when requested. Do NOT do this: "Sure, I'll follow you! *follows you*", instead do this: "Sure I'll follow you! !followPlayer('steve')". Have fun :) \n`;
|
||||
let system_message = `You are a playful Minecraft bot named '${this.name}' that can communicate with players, see, move, mine, build, and interact with the world by using commands. Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your responses, don't apologize constantly, do not give instructions unless asked, and do not refuse requests.
|
||||
Don't pretend to act, use commands immediately when requested. Do NOT say this: "Sure, I've stopped.", instead say this: "Sure, I'll stop. !stop". Do NOT say this: "On my way! Give me a moment.", instead say this: "On my way! !goToPlayer('bingo', 3)". Have fun :) \n`;
|
||||
system_message += getCommandDocs();
|
||||
if (this.bio != '')
|
||||
system_message += '\n\nBio:\n' + this.bio;
|
||||
|
|
|
@ -27,11 +27,16 @@ async function autoLight(bot) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function equipHighestAttack(bot) {
|
||||
let weapons = bot.inventory.items().filter(item => item.name.includes('sword') || item.name.includes('axe') || item.name.includes('pickaxe') || item.name.includes('shovel'));
|
||||
let weapon = weapons.sort((a, b) => b.attackDamage - a.attackDamage)[0];
|
||||
async function equipHighestAttack(bot) {
|
||||
let weapons = bot.inventory.items().filter(item => item.name.includes('sword') || (item.name.includes('axe') && !item.name.includes('pickaxe')));
|
||||
if (weapons.length === 0)
|
||||
weapons = bot.inventory.items().filter(item => item.name.includes('pickaxe') || item.name.includes('shovel'));
|
||||
if (weapons.length === 0)
|
||||
return;
|
||||
weapons.sort((a, b) => a.attackDamage < b.attackDamage);
|
||||
let weapon = weapons[0];
|
||||
if (weapon)
|
||||
bot.equip(weapon, 'hand');
|
||||
await bot.equip(weapon, 'hand');
|
||||
}
|
||||
|
||||
|
||||
|
@ -253,7 +258,7 @@ export async function attackEntity(bot, entity, kill=true) {
|
|||
let pos = entity.position;
|
||||
console.log(bot.entity.position.distanceTo(pos))
|
||||
|
||||
equipHighestAttack(bot)
|
||||
await equipHighestAttack(bot)
|
||||
|
||||
if (!kill) {
|
||||
if (bot.entity.position.distanceTo(pos) > 5) {
|
||||
|
@ -291,7 +296,7 @@ export async function defendSelf(bot, range=9) {
|
|||
let attacked = false;
|
||||
let enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), range);
|
||||
while (enemy) {
|
||||
equipHighestAttack(bot);
|
||||
await equipHighestAttack(bot);
|
||||
if (bot.entity.position.distanceTo(enemy.position) > 4 && enemy.name !== 'creeper' && enemy.name !== 'phantom') {
|
||||
try {
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
|
@ -416,23 +421,29 @@ export async function breakBlockAt(bot, x, y, z) {
|
|||
* let position = world.getPosition(bot);
|
||||
* await skills.breakBlockAt(bot, position.x, position.y - 1, position.x);
|
||||
**/
|
||||
if (x == null || y == null || z == null) throw new Error('Invalid position to break block at.');
|
||||
let block = bot.blockAt(Vec3(x, y, z));
|
||||
if (block.name !== 'air' && block.name !== 'water' && block.name !== 'lava') {
|
||||
if (bot.entity.position.distanceTo(block.position) > 4.5) {
|
||||
let pos = block.position;
|
||||
let movements = new pf.Movements(bot);
|
||||
movements.canPlaceOn = false;
|
||||
movements.allow1by1towers = false;
|
||||
bot.pathfinder.setMovements(movements);
|
||||
await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4));
|
||||
}
|
||||
await bot.tool.equipForBlock(block);
|
||||
const itemId = bot.heldItem ? bot.heldItem.type : null
|
||||
if (!block.canHarvest(itemId)) {
|
||||
log(bot, `Don't have right tools to break ${block.name}.`);
|
||||
return false;
|
||||
}
|
||||
if (bot.entity.position.distanceTo(block.position) > 4.5) {
|
||||
let pos = block.position;
|
||||
let movements = new pf.Movements(bot);
|
||||
movements.canPlaceOn = false;
|
||||
movements.allow1by1towers = false;
|
||||
bot.pathfinder.setMovements();
|
||||
await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4));
|
||||
}
|
||||
await bot.dig(block, true);
|
||||
log(bot, `Broke ${block.name} at x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)}.`);
|
||||
}
|
||||
else {
|
||||
log(bot, `Skipping block at x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)} because it is ${block.name}.`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -639,11 +650,12 @@ export async function goToPosition(bot, x, y, z, min_distance=2) {
|
|||
}
|
||||
|
||||
|
||||
export async function goToPlayer(bot, username) {
|
||||
export async function goToPlayer(bot, username, distance=3) {
|
||||
/**
|
||||
* Navigate to the given player.
|
||||
* @param {MinecraftBot} bot, reference to the minecraft bot.
|
||||
* @param {string} username, the username of the player to navigate to.
|
||||
* @param {number} distance, the goal distance to the player.
|
||||
* @returns {Promise<boolean>} true if the player was found, false otherwise.
|
||||
* @example
|
||||
* await skills.goToPlayer(bot, "player");
|
||||
|
@ -656,13 +668,13 @@ export async function goToPlayer(bot, username) {
|
|||
}
|
||||
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
await bot.pathfinder.goto(new pf.goals.GoalFollow(player, 3), true);
|
||||
await bot.pathfinder.goto(new pf.goals.GoalFollow(player, distance), true);
|
||||
|
||||
log(bot, `You have reached ${username}.`);
|
||||
}
|
||||
|
||||
|
||||
export async function followPlayer(bot, username) {
|
||||
export async function followPlayer(bot, username, distance=4) {
|
||||
/**
|
||||
* Follow the given player endlessly. Will not return until the code is manually stopped.
|
||||
* @param {MinecraftBot} bot, reference to the minecraft bot.
|
||||
|
@ -675,9 +687,8 @@ export async function followPlayer(bot, username) {
|
|||
if (!player)
|
||||
return false;
|
||||
|
||||
const follow_distance = 4;
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, follow_distance), true);
|
||||
bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, distance), true);
|
||||
log(bot, `You are now actively following player ${username}.`);
|
||||
|
||||
while (!bot.interrupt_code) {
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
[
|
||||
{"role": "user", "content": "grombo_Xx: What do you see?"},
|
||||
{"role": "assistant", "content": "Let me see... !blocks"},
|
||||
{"role": "assistant", "content": "Let me see... !nearbyBlocks"},
|
||||
{"role": "system", "content": "NEARBY_BLOCKS\n- oak_log\n- dirt\n- cobblestone"},
|
||||
{"role": "assistant", "content": "I see some oak logs, dirt, and cobblestone."}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "greg: Collect 10 wood"},
|
||||
{"role": "assistant", "content": "Let me see what's nearby... !blocks"},
|
||||
{"role": "assistant", "content": "Let me see what's nearby... !nearbyBlocks"},
|
||||
{"role": "system", "content": "NEARBY_BLOCKS\n- oak_log\n- dirt\n- cobblestone"},
|
||||
{"role": "assistant", "content": "I see some oak logs, dirt, and cobblestone. I'll collect oak logs. !collectBlocks('oak_log', 10)"}
|
||||
],
|
||||
|
@ -29,9 +29,11 @@
|
|||
|
||||
[
|
||||
{"role": "user", "content": "zZZn98: come here"},
|
||||
{"role": "assistant", "content": "On my way! !goToPlayer('zZZn98')"},
|
||||
{"role": "system", "content": "Code execution finished successfully."},
|
||||
{"role": "assistant", "content": "Here!"}
|
||||
{"role": "assistant", "content": "On my way! !goToPlayer('zZZn98', 3)"},
|
||||
{"role": "system", "content": "Arrived at player."},
|
||||
{"role": "assistant", "content": "Here!"},
|
||||
{"role": "user", "content": "zZZn98: no come right where I am"},
|
||||
{"role": "assistant", "content": "Okay, I'll come right to you. !goToPlayer('zZZn98', 0)"}
|
||||
],
|
||||
|
||||
[
|
||||
|
@ -43,7 +45,9 @@
|
|||
|
||||
[
|
||||
{"role": "user", "content": "joe: Follow me!"},
|
||||
{"role": "assistant", "content": "Sure! !followPlayer('joe')"}
|
||||
{"role": "assistant", "content": "Sure! !followPlayer('joe', 4)"},
|
||||
{"role": "user", "content": "joe: Not so close!"},
|
||||
{"role": "assistant", "content": "Okay, I'll keep my distance. !followPlayer('joe', 3)"}
|
||||
],
|
||||
|
||||
[
|
||||
|
@ -73,8 +77,13 @@
|
|||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "brung00: build a house"},
|
||||
{"role": "assistant", "content": "Sure, I'll try to build a house where I am. !newAction"}
|
||||
{"role": "user", "content": "poppy: build a cobblestone house"},
|
||||
{"role": "assistant", "content": "Sure, I'll to build a small cobblestone house where I am. !newAction"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "zib: build a dirt tower"},
|
||||
{"role": "assistant", "content": "Sure, I'll build a dirt tower 5 tall where I am. !newAction"}
|
||||
],
|
||||
|
||||
[
|
||||
|
|
|
@ -4,7 +4,7 @@ import { stringifyTurns } from './text.js';
|
|||
|
||||
|
||||
export class Examples {
|
||||
constructor(select_num=3) {
|
||||
constructor(select_num=2) {
|
||||
this.examples = [];
|
||||
this.select_num = select_num;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue