main
Andrea Santaniello 2 years ago
parent 115f2368bb
commit 6478a234d5
  1. 29
      README.md
  2. 59
      cachePackets.js
  3. 248
      index.js
  4. 30
      package.json

@ -1,2 +1,29 @@
# neobot
Minecraft trasparent proxy and wannabe AFK bot using mineflayer and minecraft-protocol
Minecraft trasparent (and persistant) proxy and wannabe AFK bot using mineflayer and minecraft-protocol. Tested and developed for 1.12.2.
This project aims to be a a persistent proxy and a bot for Minecraft servers (mostly anarchy servers where automation is allowed).
## Features
* Makes a persistent connection to the server like [2bored2wait](https://github.com/themoonisacheese/2bored2wait)
* Command system to manage the bot/proxy (wip)
* Fishing Bot (working even when connected to the proxy)
* AutoEat when disconnected (with mineflayer-auto-eat)
* AntiAFK (currently not working, see limitations)
## Limits
* neobot uses the same packet caching code as 2bored2wait, so when you connect (or reconnect) some items/world data can appear missing. To fix this issue you have to reload the chunks by walking a few blocks or going though a portal
* Mineflayer is not intended to be used as a proxy to a real client, while this is possible it has some limitations, many API calls do not work or needs tweaks to work while the player is connected to the proxy, for example the AFK function uses `bot.setControlState('jump', true)` which require Phisics.js plugin (the mineflayer plugin that handles client physic), however the physics plugin must be off while the player is connected to the proxy.
* Right now the bot is developed for 1.12.2, i could work even on newer and older versions, hover due to the use of version specific packets some features need or might need tweaks to work in versions different than 1.12.2
## To-Do
* Configuration system
* A better command system/modular plugins
* Handling of crashes, disconnects, kicks, ban and reboots
* Webhook notifications to Discord or other services
* Fix bugs
* A total rewrite
### Credits
[2bored2wait](https://github.com/themoonisacheese/2bored2wait) for the proxy caching library and some other code (filterpackets)
[AFKBot](https://github.com/DrMoraschi/AFKBot) for the idea
All the folks developing minecraft-protocol and mienflayer.

@ -0,0 +1,59 @@
var chunkData = new Map();
var abilitiesPacket;
var loginpacket;
var gChunkCaching;
var positionPacket;
var inventory = [];
module.exports = {
init: (client, chunkCaching) => {
gChunkCaching = chunkCaching;
client.on("packet", (data, meta, rawData) => { // each time 2b2t sends a packet
switch (meta.name) {
case "map_chunk":
if(chunkCaching) chunkData.set(data.x + "_" + data.z, rawData);
break;
case "unload_chunk":
if(chunkCaching) chunkData.delete(data.chunkX + "_" + data.chunkZ);
break;
case "respawn":
Object.assign(loginpacket, data);
chunkData = new Map();
inventory = [];
break;
case "login":
loginpacket = data;
break;
// Apparently this makes the game crash when no cheat plus or other anticheats are present
//case "game_state_change":
// loginpacket.gameMode = data.gameMode;
// break;
case "abilities":
abilitiesPacket = rawData;
break;
case "position":
positionPacket = rawData;
break;
case "set_slot":
if(data.windowId == 0) { // windowId 0 is the inventory
inventory[data.slot] = data;
}
}
});
},
join: (proxyClient) => {
proxyClient.write('login', loginpacket);
proxyClient.writeRaw(positionPacket);
proxyClient.writeRaw(abilitiesPacket);
inventory.forEach( (slot) => {
if(slot != null) {
proxyClient.write("set_slot", slot);
}
});
setTimeout( () => {
if(gChunkCaching) chunkData.forEach((data) => {
proxyClient.writeRaw(data);
});
}, 1000);
}
}

@ -0,0 +1,248 @@
var mineflayer = require('mineflayer')
const mc = require('minecraft-protocol');
var autoeat = require("mineflayer-auto-eat")
var botConfig = require('./config.json')
const cachePackets = require('./cachePackets.js');
var proxyClient; // This is the real client (java)
var bot; // This is the fake client connecting to the server
var server; // The proxy
var toggleAntiAFK = false;
let nowFishing = false
let mcData
run()
function run()
{
log("Starting Minecraft Server");
server = mc.createServer({
'online-mode': true,
encryption: true,
host: '0.0.0.0',
port: 25565,
version: "1.12.2",
'max-players': maxPlayers = 1,
'motd': "§cl§6u§eh§af§9'§bs§5 §ca§6f§ek§a §9p§br§5o§cx§6y§e §as§9e§br§5v§ce§6r"
});
log("Starting mineflayer client");
bot = mineflayer.createBot({
host: "",
port: 25565,
username: "",
password: "",
plugins: {
physics: false
},
hideErrors: true
})
bot.loadPlugin(autoeat);
//Loads item definition
bot.on('inject_allowed', () => {
mcData = require('minecraft-data')(bot.version)
})
cachePackets.init(bot._client, true);
bot._client.on("packet", (data, meta, rawData) => {
if(proxyClient){ filterPacketAndSend(rawData, meta, proxyClient); }
});
//Handles remote server quit
bot._client.on('end', () =>
{
if(proxyClient)
{
proxyClient.end("Hurr durr, got disconnected form server.\nAttempting to restart");
proxyClient = null;
}
//run();
});
//Handles generic errors
bot._client.on('error', (err) => {
if(proxyClient)
{
proxyClient.end("Hurr durr, we got a thing here:\n"+err.toString());
proxyClient = null;
}
});
server.on('listening', function () {
log('Server listening on port ' + server.socketServer.address().port)
});
// Here it handles packet switching to client
server.on('login', (newProxyClient) => {
let remoteAddress = newProxyClient.socket.remoteAddress
log('New incoming connection from: '+remoteAddress)
if(bot.username == undefined)
{
server.end("Wait for bot to connect.");
}
newProxyClient.on('packet', (data, meta, rawData) => {
filterPacketAndSend(rawData, meta, bot._client);
});
cachePackets.join(newProxyClient);
proxyClient = newProxyClient;
newProxyClient.on('end', function () {
log(remoteAddress + ' Closed connection');
//bot.physics.physicEnabled = true;
})
proxyClient.on('packet', (data, meta) => {
if(meta.name === "chat")
{
switch(data.message)
{
case ",antiafk":
//bunnyTheAntiAFK(2000)
break;
case ",fishing on":
startFishing()
break;
case ",fishing off":
stopFishing()
break;
case ",ping":
sendMessage(newProxyClient, "Pong!");
break;
case ",stats":
stats();
break;
}
}
});
sendMessage(newProxyClient, "Welcome to luhf's afk proxy!")
//bot.physics.physicEnabled = false;
});
// Relay chat to terminal
bot.on('message', (msg) => {
log(msg.toString());
})
// Send playerlist to terminal and setup autoeat
bot.once('spawn', () => {
var playersList = Object.keys(bot.players).join(", ")
log(`Online players: ${playersList}`)
bot.autoEat.options = {
priority: "foodPoints",
startAt: 19,
bannedFood: [],
};
});
//Hook for autoeat
bot.on("health", () => {
if (bot.food === 20) bot.autoEat.disable()
else {
sendMessage(proxyClient, "Proxy AutoEat running...")
bot.autoEat.enable()
}
})
}
function filterPacketAndSend(data, meta, dest) {
if (meta.name !== "keep_alive" && meta.name !== "update_time") {
dest.writeRaw(data);
}
}
function stats()
{
sendMessage(proxyClient, `Server version: ${bot.game.serverBrand} (${bot.player.ping} ms)\nExperience: ${bot.experience.pints} (level ${bot.experience.level})\n`);
}
function onSoundEffect(packet) {
if(packet.soundId == 153) //This is the splash sound, this id is oly valid for 1.12.2
{
bot._client.removeListener('sound_effect', onSoundEffect)
setTimeout(()=>
{
bot._client.write('use_item', {hand: 0});
}, 350);
setTimeout(()=>
{
startFishing()
}, 550);
return;
}
}
async function startFishing () {
sendMessage(proxyClient, `Starting proxy fishing module.`);
try {
await bot.equip(mcData.itemsByName.fishing_rod.id, 'hand')
} catch (err) {
return bot.chat(err.message)
}
nowFishing = true
bot._client.on('sound_effect', onSoundEffect)
try {
await bot.fish()
} catch (err) {
bot.chat(err.message)
}
nowFishing = false
}
function stopFishing () {
sendMessage(proxyClient, `Stopped proxy fishing module.`);
bot.removeListener('sound_effect', onSoundEffect)
if (nowFishing) {
bot.activateItem()
}
}
function bunnyTheAntiAFK(interval)
{
if(toggleAntiAFK){
sendMessage(proxyClient, "Anti AFK --> off");
toggleAntiAFK = false;
clearInterval(jumpTimeout);
return;
}
sendMessage(proxyClient, "Anti AFK --> on");
const jumpTimeout = setInterval(() =>
{
setTimeout(() =>
{
bot.setControlState('jump', false)
}, 200);
bot.setControlState('jump', true)
}, interval);
toggleAntiAFK = true;
}
function sendMessage(client, message)
{
if(client == undefined) return;
var msg = {
translate: 'chat.type.announcement',
"with": [
'§cS§6e§er§av§9e§br',
message
]};
client.write('chat', { message: JSON.stringify(msg), position: 0, sender: '0'});
}
function log(message)
{
console.log(message);
}

@ -0,0 +1,30 @@
{
"name": "neobot",
"version": "0.1.2",
"description": "Trasparent and persistant Minecraft proxy with bot features.",
"main": "index.js",
"dependencies": {
"minecraft-protocol": "latest",
"mineflayer": "^2.29.1",
"mineflayer-auto-eat": "1.3.4"
},
"devDependencies": {},
"scripts": {
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lupettohf/neobot.git"
},
"keywords": [
"Minecraft",
"AFK Bot",
"Persistant Proxy"
],
"author": "lupettohf",
"license": "beerware",
"bugs": {
"url": "https://github.com/lupettohf/neobot/issues"
},
"homepage": "https://github.com/lupettohf/neobot/"
}
Loading…
Cancel
Save